mesh_protobuf/
oneof.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Encoding support for protobuf `oneof` fields, which are derived from Rust
5//! enums.
6
7use crate::Error;
8use crate::FieldDecode;
9use crate::FieldEncode;
10use crate::MessageDecode;
11use crate::MessageEncode;
12use crate::Result;
13use crate::ResultExt;
14use crate::inplace::InplaceOption;
15use crate::protobuf::FieldReader;
16use crate::protobuf::MessageReader;
17use crate::protobuf::MessageSizer;
18use crate::protobuf::MessageWriter;
19use crate::protofile::DescribeField;
20use crate::protofile::DescribeMessage;
21use crate::protofile::FieldType;
22use crate::protofile::MessageDescription;
23use thiserror::Error;
24
25/// An encoder type for `oneof` fields derived from Rust enums.
26///
27/// Encoding and decoding are implemented on types that implement
28/// [`OneofEncode`] and [`OneofDecode`].
29pub struct OneofEncoder;
30
31#[derive(Debug, Error)]
32#[error("missing enum variant")]
33struct UnassignedEnum;
34
35/// A trait for encoding a `oneof` field.
36pub trait OneofEncode<R> {
37    /// Write the variant to the writer.
38    fn write_variant(self, writer: MessageWriter<'_, '_, R>);
39    /// Compute the size of the variant.
40    fn compute_variant_size(&mut self, sizer: MessageSizer<'_>);
41}
42
43/// A oneof-encoded type that has a protobuf message description.
44pub trait DescribeOneof {
45    /// The protobuf message description for this type.
46    const DESCRIPTION: MessageDescription<'static>;
47}
48
49impl<T: DescribeOneof> DescribeMessage<T> for OneofEncoder {
50    const DESCRIPTION: MessageDescription<'static> = T::DESCRIPTION;
51}
52
53impl<T: DescribeOneof> DescribeField<T> for OneofEncoder {
54    const FIELD_TYPE: FieldType<'static> = FieldType::message(|| T::DESCRIPTION);
55}
56
57impl<T: OneofEncode<R>, R> MessageEncode<T, R> for OneofEncoder {
58    fn write_message(item: T, writer: MessageWriter<'_, '_, R>) {
59        item.write_variant(writer)
60    }
61
62    fn compute_message_size(item: &mut T, sizer: MessageSizer<'_>) {
63        item.compute_variant_size(sizer)
64    }
65}
66
67impl<T: OneofEncode<R>, R> FieldEncode<T, R> for OneofEncoder {
68    fn write_field(item: T, writer: crate::protobuf::FieldWriter<'_, '_, R>) {
69        writer.message(|writer| item.write_variant(writer))
70    }
71
72    fn compute_field_size(item: &mut T, sizer: crate::protobuf::FieldSizer<'_>) {
73        sizer.message(|sizer| item.compute_variant_size(sizer))
74    }
75}
76
77/// A trait for decoding a `oneof` field.
78pub trait OneofDecode<'de, R>: Sized {
79    /// Read the specified variant from the reader.
80    fn read_variant(
81        this: &mut InplaceOption<'_, Self>,
82        number: u32,
83        reader: FieldReader<'de, '_, R>,
84    ) -> Result<()>;
85}
86
87impl<'de, T: OneofDecode<'de, R>, R> MessageDecode<'de, T, R> for OneofEncoder {
88    fn read_message(
89        item: &mut InplaceOption<'_, T>,
90        reader: MessageReader<'de, '_, R>,
91    ) -> Result<()> {
92        for field in reader {
93            let (n, field) = field.typed::<T>()?;
94            T::read_variant(item, n, field)?;
95        }
96        if item.is_none() {
97            return Err(Error::new(UnassignedEnum).typed::<T>());
98        }
99        Ok(())
100    }
101}
102
103// Manually implement this instead of using `MessageEncoding` so that we can
104// provide a simple implementation for `default_field`. This saves some
105// generated code.
106impl<'de, T: OneofDecode<'de, R>, R> FieldDecode<'de, T, R> for OneofEncoder {
107    fn read_field(item: &mut InplaceOption<'_, T>, reader: FieldReader<'de, '_, R>) -> Result<()> {
108        Self::read_message(item, reader.message()?)
109    }
110
111    fn default_field(_item: &mut InplaceOption<'_, T>) -> Result<()> {
112        Err(Error::new(UnassignedEnum).typed::<T>())
113    }
114}