1
use std::{
2
    borrow::Cow, convert::Infallible, io::ErrorKind, num::TryFromIntError, string::FromUtf8Error,
3
};
4

            
5
use arc_bytes::{
6
    serde::{Bytes, CowBytes},
7
    ArcBytes,
8
};
9
use num_traits::{FromPrimitive, ToPrimitive};
10
use ordered_varint::{Signed, Unsigned, Variable};
11

            
12
use crate::AnyError;
13

            
14
/// A trait that enables a type to convert itself to a big-endian/network byte order.
15
pub trait Key<'k>: Clone + Send + Sync {
16
    /// The error type that can be produced by either serialization or
17
    /// deserialization.
18
    type Error: AnyError;
19

            
20
    /// The size of the key, if constant.
21
    const LENGTH: Option<usize>;
22

            
23
    /// Convert `self` into a `Cow<[u8]>` containing bytes ordered in big-endian/network byte order.
24
    fn as_big_endian_bytes(&'k self) -> Result<Cow<'k, [u8]>, Self::Error>;
25

            
26
    /// Convert a slice of bytes into `Self` by interpretting `bytes` in big-endian/network byte order.
27
    fn from_big_endian_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error>;
28
}
29

            
30
impl<'k> Key<'k> for Cow<'k, [u8]> {
31
    type Error = Infallible;
32

            
33
    const LENGTH: Option<usize> = None;
34

            
35
1
    fn as_big_endian_bytes(&'k self) -> Result<Cow<'k, [u8]>, Self::Error> {
36
1
        Ok(self.clone())
37
1
    }
38

            
39
1
    fn from_big_endian_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
40
1
        Ok(Cow::Owned(bytes.to_vec()))
41
1
    }
42
}
43

            
44
impl<'a> Key<'a> for Vec<u8> {
45
    type Error = Infallible;
46

            
47
    const LENGTH: Option<usize> = None;
48

            
49
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
50
        Ok(Cow::Borrowed(self))
51
    }
52

            
53
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
54
        Ok(bytes.to_vec())
55
    }
56
}
57

            
58
impl<'a> Key<'a> for ArcBytes<'a> {
59
    type Error = Infallible;
60

            
61
    const LENGTH: Option<usize> = None;
62

            
63
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
64
        Ok(Cow::Borrowed(self))
65
    }
66

            
67
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
68
        Ok(Self::from(bytes))
69
    }
70
}
71

            
72
impl<'a> Key<'a> for CowBytes<'a> {
73
    type Error = Infallible;
74

            
75
    const LENGTH: Option<usize> = None;
76

            
77
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
78
        Ok(self.0.clone())
79
    }
80

            
81
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
82
        Ok(Self::from(bytes))
83
    }
84
}
85

            
86
impl<'a> Key<'a> for Bytes {
87
    type Error = Infallible;
88

            
89
    const LENGTH: Option<usize> = None;
90

            
91
501400
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
92
501400
        Ok(Cow::Borrowed(self))
93
501400
    }
94

            
95
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
96
        Ok(Self::from(bytes))
97
    }
98
}
99

            
100
impl<'a> Key<'a> for String {
101
    type Error = FromUtf8Error;
102

            
103
    const LENGTH: Option<usize> = None;
104

            
105
260300
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
106
260300
        Ok(Cow::Borrowed(self.as_bytes()))
107
260300
    }
108

            
109
234850
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
110
234850
        Self::from_utf8(bytes.to_vec())
111
234850
    }
112
}
113

            
114
impl<'a> Key<'a> for () {
115
    type Error = Infallible;
116

            
117
    const LENGTH: Option<usize> = Some(0);
118

            
119
51
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
120
51
        Ok(Cow::default())
121
51
    }
122

            
123
76
    fn from_big_endian_bytes(_: &'a [u8]) -> Result<Self, Self::Error> {
124
76
        Ok(())
125
76
    }
126
}
127

            
128
impl<'a> Key<'a> for bool {
129
    type Error = Infallible;
130

            
131
    const LENGTH: Option<usize> = Some(1);
132

            
133
2
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
134
2
        if *self {
135
1
            Ok(Cow::Borrowed(&[1_u8]))
136
        } else {
137
1
            Ok(Cow::Borrowed(&[0_u8]))
138
        }
139
2
    }
140

            
141
2
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
142
2
        if bytes.is_empty() || bytes[0] == 0 {
143
1
            Ok(false)
144
        } else {
145
1
            Ok(true)
146
        }
147
2
    }
148
}
149

            
150
macro_rules! impl_key_for_tuple {
151
    ($(($index:tt, $varname:ident, $generic:ident)),+) => {
152
        impl<'a, $($generic),+> Key<'a> for ($($generic),+)
153
        where
154
            $($generic: Key<'a>),+
155
        {
156
            type Error = CompositeKeyError;
157

            
158
            const LENGTH: Option<usize> = match ($($generic::LENGTH),+) {
159
                ($(Some($varname)),+) => Some($($varname +)+ 0),
160
                _ => None,
161
            };
162

            
163
508
            fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
164
508
                let mut bytes = Vec::new();
165
508

            
166
508
                $(encode_composite_key_field(&self.$index, &mut bytes)?;)+
167

            
168
508
                Ok(Cow::Owned(bytes))
169
508
            }
170

            
171
508
            fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
172
508
                $(let ($varname, bytes) = decode_composite_key_field::<$generic>(bytes)?;)+
173

            
174
508
                if bytes.is_empty() {
175
508
                    Ok(($($varname),+))
176
                } else {
177
                    Err(CompositeKeyError::new(std::io::Error::from(
178
                        ErrorKind::InvalidData,
179
                    )))
180
                }
181
508
            }
182
        }
183
    };
184
}
185

            
186
impl_key_for_tuple!((0, t1, T1), (1, t2, T2));
187
impl_key_for_tuple!((0, t1, T1), (1, t2, T2), (2, t3, T3));
188
impl_key_for_tuple!((0, t1, T1), (1, t2, T2), (2, t3, T3), (3, t4, T4));
189
impl_key_for_tuple!(
190
    (0, t1, T1),
191
    (1, t2, T2),
192
    (2, t3, T3),
193
    (3, t4, T4),
194
    (4, t5, T5)
195
);
196
impl_key_for_tuple!(
197
    (0, t1, T1),
198
    (1, t2, T2),
199
    (2, t3, T3),
200
    (3, t4, T4),
201
    (4, t5, T5),
202
    (5, t6, T6)
203
);
204
impl_key_for_tuple!(
205
    (0, t1, T1),
206
    (1, t2, T2),
207
    (2, t3, T3),
208
    (3, t4, T4),
209
    (4, t5, T5),
210
    (5, t6, T6),
211
    (6, t7, T7)
212
);
213
impl_key_for_tuple!(
214
    (0, t1, T1),
215
    (1, t2, T2),
216
    (2, t3, T3),
217
    (3, t4, T4),
218
    (4, t5, T5),
219
    (5, t6, T6),
220
    (6, t7, T7),
221
    (7, t8, T8)
222
);
223

            
224
3584
fn encode_composite_key_field<'a, T: Key<'a>>(
225
3584
    value: &'a T,
226
3584
    bytes: &mut Vec<u8>,
227
3584
) -> Result<(), CompositeKeyError> {
228
3584
    let t2 = T::as_big_endian_bytes(value).map_err(CompositeKeyError::new)?;
229
3584
    if T::LENGTH.is_none() {
230
3584
        (t2.len() as u64)
231
3584
            .encode_variable(bytes)
232
3584
            .map_err(CompositeKeyError::new)?;
233
    }
234
3584
    bytes.extend(t2.iter().copied());
235
3584
    Ok(())
236
3584
}
237

            
238
3584
fn decode_composite_key_field<'a, T: Key<'a>>(
239
3584
    mut bytes: &'a [u8],
240
3584
) -> Result<(T, &[u8]), CompositeKeyError> {
241
3584
    let length = if let Some(length) = T::LENGTH {
242
        length
243
    } else {
244
3584
        usize::try_from(u64::decode_variable(&mut bytes)?)?
245
    };
246
3584
    let (t2, remaining) = bytes.split_at(length);
247
3584
    Ok((
248
3584
        T::from_big_endian_bytes(t2).map_err(CompositeKeyError::new)?,
249
3584
        remaining,
250
    ))
251
3584
}
252

            
253
1
#[test]
254
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)] // couldn't figure out how to macro-ize it
255
1
fn composite_key_tests() {
256
7
    fn roundtrip<T: for<'a> Key<'a> + Ord + Eq + std::fmt::Debug>(mut cases: Vec<T>) {
257
7
        let mut encoded = {
258
7
            cases
259
7
                .iter()
260
508
                .map(|tuple| tuple.as_big_endian_bytes().unwrap().to_vec())
261
7
                .collect::<Vec<Vec<u8>>>()
262
7
        };
263
7
        cases.sort();
264
7
        encoded.sort();
265
7
        let decoded = encoded
266
7
            .iter()
267
508
            .map(|encoded| T::from_big_endian_bytes(encoded).unwrap())
268
7
            .collect::<Vec<_>>();
269
7
        assert_eq!(cases, decoded);
270
7
    }
271
1

            
272
1
    let values = [Unsigned::from(0_u8), Unsigned::from(16_u8)];
273
1
    let mut cases = Vec::new();
274
3
    for t1 in values {
275
6
        for t2 in values {
276
4
            cases.push((t1, t2));
277
4
        }
278
    }
279
1
    roundtrip(cases);
280
1

            
281
1
    let mut cases = Vec::new();
282
3
    for t1 in values {
283
6
        for t2 in values {
284
12
            for t3 in values {
285
8
                cases.push((t1, t2, t3));
286
8
            }
287
        }
288
    }
289
1
    roundtrip(cases);
290
1

            
291
1
    let mut cases = Vec::new();
292
3
    for t1 in values {
293
6
        for t2 in values {
294
12
            for t3 in values {
295
24
                for t4 in values {
296
16
                    cases.push((t1, t2, t3, t4));
297
16
                }
298
            }
299
        }
300
    }
301
1
    roundtrip(cases);
302
1

            
303
1
    let mut cases = Vec::new();
304
3
    for t1 in values {
305
6
        for t2 in values {
306
12
            for t3 in values {
307
24
                for t4 in values {
308
48
                    for t5 in values {
309
32
                        cases.push((t1, t2, t3, t4, t5));
310
32
                    }
311
                }
312
            }
313
        }
314
    }
315
1
    roundtrip(cases);
316
1

            
317
1
    let mut cases = Vec::new();
318
3
    for t1 in values {
319
6
        for t2 in values {
320
12
            for t3 in values {
321
24
                for t4 in values {
322
48
                    for t5 in values {
323
96
                        for t6 in values {
324
64
                            cases.push((t1, t2, t3, t4, t5, t6));
325
64
                        }
326
                    }
327
                }
328
            }
329
        }
330
    }
331
1
    roundtrip(cases);
332
1

            
333
1
    let mut cases = Vec::new();
334
3
    for t1 in values {
335
6
        for t2 in values {
336
12
            for t3 in values {
337
24
                for t4 in values {
338
48
                    for t5 in values {
339
96
                        for t6 in values {
340
192
                            for t7 in values {
341
128
                                cases.push((t1, t2, t3, t4, t5, t6, t7));
342
128
                            }
343
                        }
344
                    }
345
                }
346
            }
347
        }
348
    }
349
1
    roundtrip(cases);
350
1

            
351
1
    let mut cases = Vec::new();
352
3
    for t1 in values {
353
6
        for t2 in values {
354
12
            for t3 in values {
355
24
                for t4 in values {
356
48
                    for t5 in values {
357
96
                        for t6 in values {
358
192
                            for t7 in values {
359
384
                                for t8 in values {
360
256
                                    cases.push((t1, t2, t3, t4, t5, t6, t7, t8));
361
256
                                }
362
                            }
363
                        }
364
                    }
365
                }
366
            }
367
        }
368
    }
369
1
    roundtrip(cases);
370
1
}
371

            
372
/// An error occurred inside of one of the composite key fields.
373
#[derive(thiserror::Error, Debug)]
374
#[error("key error: {0}")]
375
pub struct CompositeKeyError(Box<dyn AnyError>);
376

            
377
impl CompositeKeyError {
378
    pub(crate) fn new<E: AnyError>(error: E) -> Self {
379
        Self(Box::new(error))
380
    }
381
}
382

            
383
impl From<TryFromIntError> for CompositeKeyError {
384
    fn from(err: TryFromIntError) -> Self {
385
        Self::new(err)
386
    }
387
}
388

            
389
impl From<std::io::Error> for CompositeKeyError {
390
    fn from(err: std::io::Error) -> Self {
391
        Self::new(err)
392
    }
393
}
394

            
395
impl<'a> Key<'a> for Signed {
396
    type Error = std::io::Error;
397

            
398
    const LENGTH: Option<usize> = None;
399

            
400
    fn as_big_endian_bytes(&self) -> Result<Cow<'a, [u8]>, Self::Error> {
401
        self.to_variable_vec().map(Cow::Owned)
402
    }
403

            
404
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
405
        Self::decode_variable(bytes)
406
    }
407
}
408

            
409
impl<'a> Key<'a> for Unsigned {
410
    type Error = std::io::Error;
411

            
412
    const LENGTH: Option<usize> = None;
413

            
414
3584
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
415
3584
        self.to_variable_vec().map(Cow::Owned)
416
3584
    }
417

            
418
3584
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
419
3584
        Self::decode_variable(bytes)
420
3584
    }
421
}
422

            
423
#[cfg(feature = "uuid")]
424
impl<'k> Key<'k> for uuid::Uuid {
425
    type Error = std::array::TryFromSliceError;
426

            
427
    const LENGTH: Option<usize> = Some(16);
428

            
429
    fn as_big_endian_bytes(&'k self) -> Result<Cow<'k, [u8]>, Self::Error> {
430
        Ok(Cow::Borrowed(self.as_bytes()))
431
    }
432

            
433
    fn from_big_endian_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
434
        Ok(Self::from_bytes(bytes.try_into()?))
435
    }
436
}
437

            
438
impl<'a, T> Key<'a> for Option<T>
439
where
440
    T: Key<'a>,
441
{
442
    type Error = T::Error;
443

            
444
    const LENGTH: Option<usize> = T::LENGTH;
445

            
446
    /// # Panics
447
    ///
448
    /// Panics if `T::into_big_endian_bytes` returns an empty `IVec`.
449
    // TODO consider removing this panic limitation by adding a single byte to
450
    // each key (at the end preferrably) so that we can distinguish between None
451
    // and a 0-byte type
452
4060
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
453
4060
        if let Some(contents) = self {
454
3434
            let contents = contents.as_big_endian_bytes()?;
455
3434
            assert!(!contents.is_empty());
456
3434
            Ok(contents)
457
        } else {
458
626
            Ok(Cow::default())
459
        }
460
4060
    }
461

            
462
4184
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
463
4184
        if bytes.is_empty() {
464
1375
            Ok(None)
465
        } else {
466
2809
            Ok(Some(T::from_big_endian_bytes(bytes)?))
467
        }
468
4184
    }
469
}
470

            
471
/// Adds `Key` support to an enum. Requires implementing
472
/// [`ToPrimitive`](num_traits::ToPrimitive) and
473
/// [`FromPrimitive`](num_traits::FromPrimitive), or using a crate like
474
/// [num-derive](https://crates.io/crates/num-derive) to do it automatically.
475
/// Take care when using enums as keys: if the order changes or if the meaning
476
/// of existing numerical values changes, make sure to update any related views'
477
/// version number to ensure the values are re-evaluated.
478
pub trait EnumKey: ToPrimitive + FromPrimitive + Clone + Send + Sync {}
479

            
480
/// An error that indicates an unexpected number of bytes were present.
481
#[derive(thiserror::Error, Debug)]
482
#[error("incorrect byte length")]
483
pub struct IncorrectByteLength;
484

            
485
/// An error that indicates an unexpected enum variant value was found.
486
#[derive(thiserror::Error, Debug)]
487
#[error("unknown enum variant")]
488
pub struct UnknownEnumVariant;
489

            
490
impl From<std::array::TryFromSliceError> for IncorrectByteLength {
491
    fn from(_: std::array::TryFromSliceError) -> Self {
492
        Self
493
    }
494
}
495

            
496
// ANCHOR: impl_key_for_enumkey
497
impl<'a, T> Key<'a> for T
498
where
499
    T: EnumKey,
500
{
501
    type Error = std::io::Error;
502
    const LENGTH: Option<usize> = None;
503

            
504
4
    fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
505
4
        let integer = self
506
4
            .to_u64()
507
4
            .map(Unsigned::from)
508
4
            .ok_or_else(|| std::io::Error::new(ErrorKind::InvalidData, IncorrectByteLength))?;
509
4
        Ok(Cow::Owned(integer.to_variable_vec()?))
510
4
    }
511

            
512
2
    fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
513
2
        let primitive = u64::decode_variable(bytes)?;
514
2
        Self::from_u64(primitive)
515
2
            .ok_or_else(|| std::io::Error::new(ErrorKind::InvalidData, UnknownEnumVariant))
516
2
    }
517
}
518
// ANCHOR_END: impl_key_for_enumkey
519

            
520
macro_rules! impl_key_for_primitive {
521
    ($type:ident) => {
522
        impl<'a> Key<'a> for $type {
523
            type Error = IncorrectByteLength;
524
            const LENGTH: Option<usize> = Some(std::mem::size_of::<$type>());
525

            
526
2273206
            fn as_big_endian_bytes(&'a self) -> Result<Cow<'a, [u8]>, Self::Error> {
527
2273206
                Ok(Cow::from(self.to_be_bytes().to_vec()))
528
2273206
            }
529

            
530
152373
            fn from_big_endian_bytes(bytes: &'a [u8]) -> Result<Self, Self::Error> {
531
152373
                Ok($type::from_be_bytes(bytes.try_into()?))
532
152373
            }
533
        }
534
    };
535
}
536

            
537
impl_key_for_primitive!(i8);
538
impl_key_for_primitive!(u8);
539
impl_key_for_primitive!(i16);
540
impl_key_for_primitive!(u16);
541
impl_key_for_primitive!(i32);
542
impl_key_for_primitive!(u32);
543
impl_key_for_primitive!(i64);
544
impl_key_for_primitive!(u64);
545
impl_key_for_primitive!(i128);
546
impl_key_for_primitive!(u128);
547

            
548
1
#[test]
549
#[allow(clippy::cognitive_complexity)] // I disagree - @ecton
550
1
fn primitive_key_encoding_tests() -> anyhow::Result<()> {
551
1
    macro_rules! test_primitive_extremes {
552
1
        ($type:ident) => {
553
1
            assert_eq!(
554
1
                &$type::MAX.to_be_bytes(),
555
1
                $type::MAX.as_big_endian_bytes()?.as_ref()
556
1
            );
557
1
            assert_eq!(
558
1
                $type::MAX,
559
1
                $type::from_big_endian_bytes(&$type::MAX.as_big_endian_bytes()?)?
560
1
            );
561
1
            assert_eq!(
562
1
                $type::MIN,
563
1
                $type::from_big_endian_bytes(&$type::MIN.as_big_endian_bytes()?)?
564
1
            );
565
1
        };
566
1
    }
567
1

            
568
1
    test_primitive_extremes!(i8);
569
1
    test_primitive_extremes!(u8);
570
1
    test_primitive_extremes!(i16);
571
1
    test_primitive_extremes!(u16);
572
1
    test_primitive_extremes!(i32);
573
1
    test_primitive_extremes!(u32);
574
1
    test_primitive_extremes!(i64);
575
1
    test_primitive_extremes!(u64);
576
1
    test_primitive_extremes!(i128);
577
1
    test_primitive_extremes!(u128);
578

            
579
1
    Ok(())
580
1
}
581

            
582
1
#[test]
583
1
fn optional_key_encoding_tests() -> anyhow::Result<()> {
584
1
    assert!(Option::<i8>::None.as_big_endian_bytes()?.is_empty());
585
1
    assert_eq!(
586
1
        Some(1_i8),
587
1
        Option::from_big_endian_bytes(&Some(1_i8).as_big_endian_bytes()?)?
588
    );
589
1
    Ok(())
590
1
}
591

            
592
1
#[test]
593
#[allow(clippy::unit_cmp)] // this is more of a compilation test
594
1
fn unit_key_encoding_tests() -> anyhow::Result<()> {
595
1
    assert!(().as_big_endian_bytes()?.is_empty());
596
1
    assert_eq!((), <() as Key>::from_big_endian_bytes(&[])?);
597
1
    Ok(())
598
1
}
599

            
600
1
#[test]
601
#[allow(clippy::unit_cmp)] // this is more of a compilation test
602
1
fn bool_key_encoding_tests() -> anyhow::Result<()> {
603
1
    let true_as_bytes = true.as_big_endian_bytes()?;
604
1
    let false_as_bytes = false.as_big_endian_bytes()?;
605
1
    assert!(bool::from_big_endian_bytes(&true_as_bytes)?);
606
1
    assert!(!bool::from_big_endian_bytes(&false_as_bytes)?);
607
1
    Ok(())
608
1
}
609

            
610
1
#[test]
611
1
fn vec_key_encoding_tests() -> anyhow::Result<()> {
612
1
    const ORIGINAL_VALUE: &[u8] = b"bonsaidb";
613
1
    let vec = Cow::<'_, [u8]>::from(ORIGINAL_VALUE);
614
1
    assert_eq!(
615
1
        vec.clone(),
616
1
        Cow::from_big_endian_bytes(&vec.as_big_endian_bytes()?)?
617
    );
618
1
    Ok(())
619
1
}
620

            
621
1
#[test]
622
#[allow(clippy::use_self)] // Weird interaction with num_derive
623
1
fn enum_derive_tests() -> anyhow::Result<()> {
624
4
    #[derive(Clone, num_derive::ToPrimitive, num_derive::FromPrimitive)]
625
    enum SomeEnum {
626
        One = 1,
627
        NineNineNine = 999,
628
    }
629

            
630
    impl EnumKey for SomeEnum {}
631

            
632
1
    let encoded = SomeEnum::One.as_big_endian_bytes()?;
633
1
    let value = SomeEnum::from_big_endian_bytes(&encoded)?;
634
1
    assert!(matches!(value, SomeEnum::One));
635

            
636
1
    let encoded = SomeEnum::NineNineNine.as_big_endian_bytes()?;
637
1
    let value = SomeEnum::from_big_endian_bytes(&encoded)?;
638
1
    assert!(matches!(value, SomeEnum::NineNineNine));
639

            
640
1
    Ok(())
641
1
}