protobuf_core/field/
write.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Field writing utilities for Protocol Buffers
16//!
17//! This module provides low-level utilities for writing raw protobuf fields to byte streams.
18
19use crate::Result;
20use crate::field::{Field, FieldValue};
21use crate::tag::Tag;
22use crate::varint::{Varint, WriteExtVarint};
23use crate::wire_format::WireType;
24use ::std::io::Write;
25
26/// Extension trait for writing raw Protocol Buffer fields to `Write` types
27///
28/// This trait provides low-level utilities for writing fields to a byte stream.
29/// It is the counterpart to `ReadExtProtobuf` for serialization.
30pub trait WriteExtProtobuf {
31    /// Write a single raw protobuf field to the writer (tag + value)
32    ///
33    /// Returns the number of bytes written.
34    ///
35    /// # Example
36    /// ```
37    /// use protobuf_core::field::{WriteExtProtobuf, Field, FieldValue};
38    /// use protobuf_core::field_number::FieldNumber;
39    ///
40    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
41    ///     let mut buffer = Vec::new();
42    ///     
43    ///     let field = Field::new(
44    ///         FieldNumber::try_from(1)?,
45    ///         FieldValue::from_uint64(150)
46    ///     );
47    ///     
48    ///     buffer.write_protobuf_field(&field)?;
49    ///     assert_eq!(buffer, vec![0x08, 0x96, 0x01]); // field 1: 150
50    ///     Ok(())
51    /// }
52    /// ```
53    fn write_protobuf_field(&mut self, field: &Field) -> Result<usize>;
54
55    /// Write multiple raw protobuf fields to the writer
56    ///
57    /// Returns the total number of bytes written.
58    ///
59    /// # Example
60    /// ```
61    /// use protobuf_core::field::{WriteExtProtobuf, Field, FieldValue};
62    /// use protobuf_core::field_number::FieldNumber;
63    ///
64    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
65    ///     let mut buffer = Vec::new();
66    ///     
67    ///     let fields = vec![
68    ///         Field::new(FieldNumber::try_from(1)?, FieldValue::from_uint64(150)),
69    ///         Field::new(FieldNumber::try_from(2)?, FieldValue::from_string("Hello".to_string())),
70    ///     ];
71    ///     
72    ///     buffer.write_protobuf_fields(&fields)?;
73    ///     Ok(())
74    /// }
75    /// ```
76    fn write_protobuf_fields<'a, I>(&mut self, fields: I) -> Result<usize>
77    where
78        I: IntoIterator<Item = &'a Field>;
79}
80
81impl<W> WriteExtProtobuf for W
82where
83    W: Write,
84{
85    fn write_protobuf_field(&mut self, field: &Field) -> Result<usize> {
86        let mut bytes_written = 0;
87
88        // Write tag
89        let wire_type = match &field.value {
90            FieldValue::Varint(_) => WireType::Varint,
91            FieldValue::I32(_) => WireType::Int32,
92            FieldValue::I64(_) => WireType::Int64,
93            FieldValue::Len(_) => WireType::Len,
94        };
95        let tag = Tag {
96            field_number: field.field_number,
97            wire_type,
98        };
99        let tag_varint = tag.to_encoded();
100        bytes_written += self.write_varint(&tag_varint)?;
101
102        // Write value
103        match &field.value {
104            FieldValue::Varint(varint) => {
105                bytes_written += self.write_varint(varint)?;
106            }
107            FieldValue::I32(bytes) => {
108                self.write_all(bytes)?;
109                bytes_written += 4;
110            }
111            FieldValue::I64(bytes) => {
112                self.write_all(bytes)?;
113                bytes_written += 8;
114            }
115            FieldValue::Len(data) => {
116                // Write length
117                let length_varint = Varint::from_uint64(data.len() as u64);
118                bytes_written += self.write_varint(&length_varint)?;
119                // Write data
120                self.write_all(data)?;
121                bytes_written += data.len();
122            }
123        }
124
125        Ok(bytes_written)
126    }
127
128    fn write_protobuf_fields<'a, I>(&mut self, fields: I) -> Result<usize>
129    where
130        I: IntoIterator<Item = &'a Field>,
131    {
132        let mut total_bytes = 0;
133        for field in fields {
134            total_bytes += self.write_protobuf_field(field)?;
135        }
136        Ok(total_bytes)
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::field_number::FieldNumber;
144
145    #[test]
146    fn test_write_single_varint_field() {
147        let mut buffer = Vec::new();
148
149        let field = Field::new(
150            FieldNumber::try_from(1).unwrap(),
151            FieldValue::from_uint64(150),
152        );
153
154        let bytes_written = buffer.write_protobuf_field(&field).unwrap();
155        assert_eq!(bytes_written, 3); // tag (1 byte) + value (2 bytes)
156        assert_eq!(buffer, vec![0x08, 0x96, 0x01]); // field 1: 150
157    }
158
159    #[test]
160    fn test_write_len_field() {
161        let mut buffer = Vec::new();
162
163        let field = Field::new(
164            FieldNumber::try_from(2).unwrap(),
165            FieldValue::from_string("Hel".to_string()),
166        );
167
168        buffer.write_protobuf_field(&field).unwrap();
169        assert_eq!(buffer, vec![0x12, 0x03, 0x48, 0x65, 0x6c]); // field 2: "Hel"
170    }
171
172    #[test]
173    fn test_write_i32_field() {
174        let mut buffer = Vec::new();
175
176        let field = Field::new(
177            FieldNumber::try_from(2).unwrap(),
178            FieldValue::from_fixed32(0x12345678),
179        );
180
181        buffer.write_protobuf_field(&field).unwrap();
182        assert_eq!(buffer, vec![0x15, 0x78, 0x56, 0x34, 0x12]); // field 2: 0x12345678
183    }
184
185    #[test]
186    fn test_write_i64_field() {
187        let mut buffer = Vec::new();
188
189        let field = Field::new(
190            FieldNumber::try_from(3).unwrap(),
191            FieldValue::from_fixed64(0x1234567890ABCDEF),
192        );
193
194        buffer.write_protobuf_field(&field).unwrap();
195        assert_eq!(
196            buffer,
197            vec![0x19, 0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12]
198        ); // field 3: 0x1234567890ABCDEF
199    }
200
201    #[test]
202    fn test_write_multiple_fields() {
203        let mut buffer = Vec::new();
204
205        let fields = vec![
206            Field::new(
207                FieldNumber::try_from(1).unwrap(),
208                FieldValue::from_uint64(150),
209            ),
210            Field::new(
211                FieldNumber::try_from(2).unwrap(),
212                FieldValue::from_string("Hel".to_string()),
213            ),
214        ];
215
216        buffer.write_protobuf_fields(&fields).unwrap();
217        assert_eq!(
218            buffer,
219            vec![
220                0x08, 0x96, 0x01, // field 1: 150
221                0x12, 0x03, 0x48, 0x65, 0x6c, // field 2: "Hel"
222            ]
223        );
224    }
225
226    #[test]
227    fn test_field_encoded_size() {
228        let field = Field::new(
229            FieldNumber::try_from(1).unwrap(),
230            FieldValue::from_uint64(150),
231        );
232        assert_eq!(field.encoded_size(), 3); // tag (1 byte) + value (2 bytes)
233
234        let field = Field::new(
235            FieldNumber::try_from(2).unwrap(),
236            FieldValue::from_string("Hello".to_string()),
237        );
238        assert_eq!(field.encoded_size(), 7); // tag (1 byte) + length (1 byte) + data (5 bytes)
239    }
240
241    #[test]
242    fn test_fieldvalue_constructors() {
243        // Varint types
244        assert!(matches!(FieldValue::from_uint64(42), FieldValue::Varint(_)));
245        assert!(matches!(FieldValue::from_uint32(42), FieldValue::Varint(_)));
246        assert!(matches!(
247            FieldValue::from_sint64(-42),
248            FieldValue::Varint(_)
249        ));
250        assert!(matches!(
251            FieldValue::from_sint32(-42),
252            FieldValue::Varint(_)
253        ));
254        assert!(matches!(FieldValue::from_int64(-42), FieldValue::Varint(_)));
255        assert!(matches!(FieldValue::from_int32(-42), FieldValue::Varint(_)));
256        assert!(matches!(FieldValue::from_bool(true), FieldValue::Varint(_)));
257
258        // Fixed-width types
259        assert!(matches!(FieldValue::from_fixed32(42), FieldValue::I32(_)));
260        assert!(matches!(FieldValue::from_sfixed32(-42), FieldValue::I32(_)));
261        assert!(matches!(FieldValue::from_float(3.14), FieldValue::I32(_)));
262        assert!(matches!(FieldValue::from_fixed64(42), FieldValue::I64(_)));
263        assert!(matches!(FieldValue::from_sfixed64(-42), FieldValue::I64(_)));
264        assert!(matches!(FieldValue::from_double(3.14), FieldValue::I64(_)));
265
266        // Length-delimited types
267        assert!(matches!(
268            FieldValue::from_bytes(vec![1, 2, 3]),
269            FieldValue::Len(_)
270        ));
271        assert!(matches!(
272            FieldValue::from_string("test".to_string()),
273            FieldValue::Len(_)
274        ));
275    }
276
277    // Roundtrip tests (require both read and write features)
278    #[cfg(feature = "read")]
279    mod roundtrip_tests {
280        use super::*;
281        use crate::field::read::ReadExtProtobuf;
282
283        #[test]
284        fn test_roundtrip_varint() {
285            let mut buffer = Vec::new();
286
287            // Write
288            let original_field = Field::new(
289                FieldNumber::try_from(1).unwrap(),
290                FieldValue::from_uint64(150),
291            );
292            buffer.write_protobuf_field(&original_field).unwrap();
293
294            // Read
295            let mut reader = buffer.as_slice();
296            let read_field = reader.read_protobuf_field().unwrap().unwrap();
297
298            assert_eq!(read_field, original_field);
299        }
300
301        #[test]
302        fn test_roundtrip_string() {
303            let mut buffer = Vec::new();
304
305            // Write
306            let original_field = Field::new(
307                FieldNumber::try_from(2).unwrap(),
308                FieldValue::from_string("Hello, Protocol Buffers!".to_string()),
309            );
310            buffer.write_protobuf_field(&original_field).unwrap();
311
312            // Read
313            let mut reader = buffer.as_slice();
314            let read_field = reader.read_protobuf_field().unwrap().unwrap();
315
316            assert_eq!(read_field, original_field);
317        }
318
319        #[test]
320        fn test_roundtrip_multiple_fields() {
321            let mut buffer = Vec::new();
322
323            // Write
324            let original_fields = vec![
325                Field::new(
326                    FieldNumber::try_from(1).unwrap(),
327                    FieldValue::from_uint64(150),
328                ),
329                Field::new(
330                    FieldNumber::try_from(2).unwrap(),
331                    FieldValue::from_string("Hello".to_string()),
332                ),
333                Field::new(
334                    FieldNumber::try_from(3).unwrap(),
335                    FieldValue::from_fixed32(0x12345678),
336                ),
337            ];
338            buffer.write_protobuf_fields(&original_fields).unwrap();
339
340            // Read
341            let reader = buffer.as_slice();
342            let read_fields: Vec<_> = reader
343                .read_protobuf_fields()
344                .collect::<std::result::Result<Vec<_>, _>>()
345                .unwrap();
346
347            assert_eq!(read_fields, original_fields);
348        }
349    }
350}