Skip to main content

scirs2_io/schema_registry/
wire.rs

1//! Pure-Rust Protocol Buffer wire format encoder and decoder.
2//!
3//! This module implements the proto3 wire format at the level required by the
4//! schema registry: varint encoding/decoding, field tagging, fixed-width
5//! fields, and length-delimited payloads.  It deliberately avoids the `prost`
6//! crate to remain dependency-free (COOLJAPAN pure-Rust policy).
7//!
8//! # Wire type table
9//!
10//! | ID | Meaning          | Rust type     |
11//! |----|------------------|---------------|
12//! | 0  | Varint           | u64           |
13//! | 1  | 64-bit fixed     | [u8; 8]       |
14//! | 2  | Length-delimited | `Vec<u8>`     |
15//! | 5  | 32-bit fixed     | [u8; 4]       |
16//!
17//! Wire types 3 and 4 (start/end group) are not supported.
18
19use super::types::{
20    FieldDescriptor, FieldType, FieldValue, MessageDescriptor, SchemaRegistryError,
21    SchemaRegistryResult,
22};
23
24// ─── Wire type ───────────────────────────────────────────────────────────────
25
26/// The four wire types used in this implementation.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum WireType {
30    /// Wire type 0 — variable-length integer (LEB-128).
31    Varint,
32    /// Wire type 1 — 64-bit little-endian fixed-width.
33    Fixed64,
34    /// Wire type 2 — length-delimited byte sequence.
35    LengthDelimited,
36    /// Wire type 5 — 32-bit little-endian fixed-width.
37    Fixed32,
38}
39
40impl WireType {
41    /// Return the numeric wire-type id as used in the proto tag varint.
42    pub fn id(self) -> u32 {
43        match self {
44            WireType::Varint => 0,
45            WireType::Fixed64 => 1,
46            WireType::LengthDelimited => 2,
47            WireType::Fixed32 => 5,
48        }
49    }
50
51    /// Parse a numeric wire-type id.
52    pub fn from_id(id: u32) -> SchemaRegistryResult<Self> {
53        match id {
54            0 => Ok(WireType::Varint),
55            1 => Ok(WireType::Fixed64),
56            2 => Ok(WireType::LengthDelimited),
57            5 => Ok(WireType::Fixed32),
58            _ => Err(SchemaRegistryError::WireFormat(format!(
59                "unknown wire type id: {id}"
60            ))),
61        }
62    }
63
64    /// Return the wire type implied by a [`FieldType`].
65    pub fn for_field_type(ft: &FieldType) -> Self {
66        match ft {
67            FieldType::Int32
68            | FieldType::Int64
69            | FieldType::UInt32
70            | FieldType::UInt64
71            | FieldType::Bool => WireType::Varint,
72            FieldType::Float => WireType::Fixed32,
73            FieldType::Double => WireType::Fixed64,
74            FieldType::String
75            | FieldType::Bytes
76            | FieldType::Message(_)
77            | FieldType::Repeated(_) => WireType::LengthDelimited,
78        }
79    }
80}
81
82// ─── WireValue ────────────────────────────────────────────────────────────────
83
84/// A raw decoded wire value before schema-guided interpretation.
85#[derive(Debug, Clone, PartialEq)]
86#[non_exhaustive]
87pub enum WireValue {
88    /// Wire type 0 payload.
89    Varint(u64),
90    /// Wire type 1 payload (little-endian bytes).
91    Fixed64([u8; 8]),
92    /// Wire type 2 payload.
93    LengthDelimited(Vec<u8>),
94    /// Wire type 5 payload (little-endian bytes).
95    Fixed32([u8; 4]),
96}
97
98// ─── Varint primitives ───────────────────────────────────────────────────────
99
100/// Encode `value` as a Protocol Buffer varint (LEB-128 variant) and append it
101/// to `buf`.
102pub fn encode_varint(value: u64, buf: &mut Vec<u8>) {
103    let mut v = value;
104    loop {
105        let byte = (v & 0x7f) as u8;
106        v >>= 7;
107        if v == 0 {
108            buf.push(byte);
109            break;
110        }
111        buf.push(byte | 0x80);
112    }
113}
114
115/// Decode a varint from `buf` starting at `*pos`.  On success, advances
116/// `*pos` past the varint bytes and returns the decoded value.  Returns
117/// `None` on truncated input or overflow.
118pub fn decode_varint(buf: &[u8], pos: &mut usize) -> Option<u64> {
119    let mut result: u64 = 0;
120    let mut shift: u32 = 0;
121    loop {
122        if *pos >= buf.len() || shift >= 64 {
123            return None;
124        }
125        let byte = buf[*pos];
126        *pos += 1;
127        result |= ((byte & 0x7f) as u64) << shift;
128        shift += 7;
129        if byte & 0x80 == 0 {
130            return Some(result);
131        }
132    }
133}
134
135// ─── Field tag encoding ──────────────────────────────────────────────────────
136
137/// Encode the field tag (field_number << 3 | wire_type) and append to `buf`.
138pub fn encode_field(field_number: u32, wire_type: WireType, buf: &mut Vec<u8>) {
139    let tag: u64 = ((field_number as u64) << 3) | (wire_type.id() as u64);
140    encode_varint(tag, buf);
141}
142
143// ─── ProtoEncoder ────────────────────────────────────────────────────────────
144
145/// Fluent builder for constructing Protocol Buffer messages byte-by-byte.
146///
147/// Each method encodes one field (tag + payload) and returns `&mut Self` for
148/// chaining.  Call [`build`](ProtoEncoder::build) to extract the completed byte
149/// vector.
150///
151/// # Example
152///
153/// ```rust
154/// use scirs2_io::schema_registry::wire::ProtoEncoder;
155///
156/// let bytes = ProtoEncoder::new()
157///     .int64(1, 12345)
158///     .string(2, "hello world")
159///     .build();
160/// assert!(!bytes.is_empty());
161/// ```
162#[derive(Debug, Default)]
163pub struct ProtoEncoder {
164    buf: Vec<u8>,
165}
166
167impl ProtoEncoder {
168    /// Create a new, empty encoder.
169    pub fn new() -> Self {
170        Self { buf: Vec::new() }
171    }
172
173    /// Encode a signed 32-bit integer as a varint (wire type 0).
174    pub fn int32(mut self, field_number: u32, value: i32) -> Self {
175        encode_field(field_number, WireType::Varint, &mut self.buf);
176        encode_varint(value as u64, &mut self.buf);
177        self
178    }
179
180    /// Encode a signed 64-bit integer as a varint (wire type 0).
181    pub fn int64(mut self, field_number: u32, value: i64) -> Self {
182        encode_field(field_number, WireType::Varint, &mut self.buf);
183        encode_varint(value as u64, &mut self.buf);
184        self
185    }
186
187    /// Encode an unsigned 32-bit integer as a varint (wire type 0).
188    pub fn uint32(mut self, field_number: u32, value: u32) -> Self {
189        encode_field(field_number, WireType::Varint, &mut self.buf);
190        encode_varint(value as u64, &mut self.buf);
191        self
192    }
193
194    /// Encode an unsigned 64-bit integer as a varint (wire type 0).
195    pub fn uint64(mut self, field_number: u32, value: u64) -> Self {
196        encode_field(field_number, WireType::Varint, &mut self.buf);
197        encode_varint(value, &mut self.buf);
198        self
199    }
200
201    /// Encode a boolean as a varint (wire type 0).
202    pub fn bool(mut self, field_number: u32, value: bool) -> Self {
203        encode_field(field_number, WireType::Varint, &mut self.buf);
204        encode_varint(value as u64, &mut self.buf);
205        self
206    }
207
208    /// Encode a 32-bit float (wire type 5).
209    pub fn float(mut self, field_number: u32, value: f32) -> Self {
210        encode_field(field_number, WireType::Fixed32, &mut self.buf);
211        self.buf.extend_from_slice(&value.to_le_bytes());
212        self
213    }
214
215    /// Encode a 64-bit float (wire type 1).
216    pub fn double(mut self, field_number: u32, value: f64) -> Self {
217        encode_field(field_number, WireType::Fixed64, &mut self.buf);
218        self.buf.extend_from_slice(&value.to_le_bytes());
219        self
220    }
221
222    /// Encode a UTF-8 string as a length-delimited field (wire type 2).
223    pub fn string(mut self, field_number: u32, value: &str) -> Self {
224        self.write_length_delimited(field_number, value.as_bytes());
225        self
226    }
227
228    /// Encode a raw byte sequence as a length-delimited field (wire type 2).
229    pub fn bytes(mut self, field_number: u32, value: &[u8]) -> Self {
230        self.write_length_delimited(field_number, value);
231        self
232    }
233
234    /// Encode a pre-encoded nested message as a length-delimited field (wire type 2).
235    pub fn message(mut self, field_number: u32, nested_bytes: &[u8]) -> Self {
236        self.write_length_delimited(field_number, nested_bytes);
237        self
238    }
239
240    /// Consume the encoder and return the completed byte vector.
241    pub fn build(self) -> Vec<u8> {
242        self.buf
243    }
244
245    // ── Internal helpers ────────────────────────────────────────────────────
246
247    fn write_length_delimited(&mut self, field_number: u32, data: &[u8]) {
248        encode_field(field_number, WireType::LengthDelimited, &mut self.buf);
249        encode_varint(data.len() as u64, &mut self.buf);
250        self.buf.extend_from_slice(data);
251    }
252}
253
254// ─── ProtoDecoder ────────────────────────────────────────────────────────────
255
256/// Iterator-style decoder over a Protocol Buffer wire-format byte slice.
257///
258/// Each call to [`next_field`](ProtoDecoder::next_field) consumes one complete
259/// field (tag + payload) from the buffer and returns `(field_number, WireValue)`.
260pub struct ProtoDecoder<'a> {
261    buf: &'a [u8],
262    pos: usize,
263}
264
265impl<'a> ProtoDecoder<'a> {
266    /// Create a new decoder over `buf`.
267    pub fn new(buf: &'a [u8]) -> Self {
268        Self { buf, pos: 0 }
269    }
270
271    /// Returns `true` when the entire buffer has been consumed.
272    pub fn is_empty(&self) -> bool {
273        self.pos >= self.buf.len()
274    }
275
276    /// Decode the next field from the buffer.
277    ///
278    /// Returns `None` at end-of-buffer, or `Some(Err(_))` on a malformed
279    /// message.
280    pub fn next_field(&mut self) -> Option<SchemaRegistryResult<(u32, WireValue)>> {
281        if self.is_empty() {
282            return None;
283        }
284
285        // Decode tag varint
286        let tag = decode_varint(self.buf, &mut self.pos)?;
287        let wire_type_id = (tag & 0x07) as u32;
288        let field_number = (tag >> 3) as u32;
289
290        let wire_type = match WireType::from_id(wire_type_id) {
291            Ok(wt) => wt,
292            Err(e) => return Some(Err(e)),
293        };
294
295        let value = match self.decode_payload(wire_type) {
296            Ok(v) => v,
297            Err(e) => return Some(Err(e)),
298        };
299
300        Some(Ok((field_number, value)))
301    }
302
303    /// Decode all remaining fields into a `Vec`.
304    pub fn collect_all(&mut self) -> SchemaRegistryResult<Vec<(u32, WireValue)>> {
305        let mut out = Vec::new();
306        while let Some(result) = self.next_field() {
307            out.push(result?);
308        }
309        Ok(out)
310    }
311
312    // ── Internal helpers ────────────────────────────────────────────────────
313
314    fn decode_payload(&mut self, wire_type: WireType) -> SchemaRegistryResult<WireValue> {
315        match wire_type {
316            WireType::Varint => {
317                let v = decode_varint(self.buf, &mut self.pos).ok_or_else(|| {
318                    SchemaRegistryError::WireFormat("truncated varint".to_string())
319                })?;
320                Ok(WireValue::Varint(v))
321            }
322            WireType::Fixed64 => {
323                if self.pos + 8 > self.buf.len() {
324                    return Err(SchemaRegistryError::WireFormat(
325                        "truncated fixed64 field".to_string(),
326                    ));
327                }
328                let mut b = [0u8; 8];
329                b.copy_from_slice(&self.buf[self.pos..self.pos + 8]);
330                self.pos += 8;
331                Ok(WireValue::Fixed64(b))
332            }
333            WireType::LengthDelimited => {
334                let len = decode_varint(self.buf, &mut self.pos).ok_or_else(|| {
335                    SchemaRegistryError::WireFormat(
336                        "truncated length prefix in length-delimited field".to_string(),
337                    )
338                })? as usize;
339
340                if self.pos + len > self.buf.len() {
341                    return Err(SchemaRegistryError::WireFormat(format!(
342                        "length-delimited field claims {len} bytes but only {} remain",
343                        self.buf.len() - self.pos
344                    )));
345                }
346                let payload = self.buf[self.pos..self.pos + len].to_vec();
347                self.pos += len;
348                Ok(WireValue::LengthDelimited(payload))
349            }
350            WireType::Fixed32 => {
351                if self.pos + 4 > self.buf.len() {
352                    return Err(SchemaRegistryError::WireFormat(
353                        "truncated fixed32 field".to_string(),
354                    ));
355                }
356                let mut b = [0u8; 4];
357                b.copy_from_slice(&self.buf[self.pos..self.pos + 4]);
358                self.pos += 4;
359                Ok(WireValue::Fixed32(b))
360            }
361        }
362    }
363}
364
365// ─── Schema-guided encode / decode ────────────────────────────────────────────
366
367/// Encode a set of `(field_number, FieldValue)` pairs according to the message
368/// descriptor and return the wire-format bytes.
369///
370/// Fields not declared in the descriptor are silently skipped.  The field
371/// values are encoded in the order they appear in `values`.
372pub fn encode_message(schema: &MessageDescriptor, values: &[(u32, FieldValue)]) -> Vec<u8> {
373    let mut buf = Vec::new();
374
375    for (field_number, value) in values {
376        // Only encode fields that appear in the descriptor.
377        let field_desc = match schema.field_by_number(*field_number) {
378            Some(fd) => fd,
379            None => continue,
380        };
381
382        encode_field_value(*field_number, &field_desc.field_type, value, &mut buf);
383    }
384
385    buf
386}
387
388/// Decode wire-format `bytes` into a list of `(field_name, FieldValue)` pairs
389/// using the supplied message descriptor for type guidance.
390///
391/// Unknown field numbers (not in the descriptor) are silently skipped — this
392/// mirrors proto3 unknown-field handling and allows forward-compatible readers.
393pub fn decode_message(
394    schema: &MessageDescriptor,
395    bytes: &[u8],
396) -> SchemaRegistryResult<Vec<(std::string::String, FieldValue)>> {
397    let mut decoder = ProtoDecoder::new(bytes);
398    let raw_fields = decoder.collect_all()?;
399    let mut out = Vec::new();
400
401    for (field_number, wire_value) in raw_fields {
402        let field_desc = match schema.field_by_number(field_number) {
403            Some(fd) => fd,
404            None => continue, // Unknown field — skip
405        };
406
407        let field_value = wire_value_to_field_value(&field_desc.field_type, wire_value)?;
408        out.push((field_desc.name.clone(), field_value));
409    }
410
411    Ok(out)
412}
413
414// ─── Internal helpers ────────────────────────────────────────────────────────
415
416fn encode_field_value(
417    field_number: u32,
418    field_type: &FieldType,
419    value: &FieldValue,
420    buf: &mut Vec<u8>,
421) {
422    match (field_type, value) {
423        (FieldType::Int32, FieldValue::Int32(v)) => {
424            encode_field(field_number, WireType::Varint, buf);
425            encode_varint(*v as u64, buf);
426        }
427        (FieldType::Int64, FieldValue::Int64(v)) => {
428            encode_field(field_number, WireType::Varint, buf);
429            encode_varint(*v as u64, buf);
430        }
431        (FieldType::Int64, FieldValue::Int32(v)) => {
432            // Widening: int32 into int64 field
433            encode_field(field_number, WireType::Varint, buf);
434            encode_varint(*v as u64, buf);
435        }
436        (FieldType::UInt32, FieldValue::UInt32(v)) => {
437            encode_field(field_number, WireType::Varint, buf);
438            encode_varint(*v as u64, buf);
439        }
440        (FieldType::UInt64, FieldValue::UInt64(v)) => {
441            encode_field(field_number, WireType::Varint, buf);
442            encode_varint(*v, buf);
443        }
444        (FieldType::UInt64, FieldValue::UInt32(v)) => {
445            encode_field(field_number, WireType::Varint, buf);
446            encode_varint(*v as u64, buf);
447        }
448        (FieldType::Bool, FieldValue::Bool(v)) => {
449            encode_field(field_number, WireType::Varint, buf);
450            encode_varint(*v as u64, buf);
451        }
452        (FieldType::Float, FieldValue::Float(v)) => {
453            encode_field(field_number, WireType::Fixed32, buf);
454            buf.extend_from_slice(&v.to_le_bytes());
455        }
456        (FieldType::Double, FieldValue::Double(v)) => {
457            encode_field(field_number, WireType::Fixed64, buf);
458            buf.extend_from_slice(&v.to_le_bytes());
459        }
460        (FieldType::Double, FieldValue::Float(v)) => {
461            encode_field(field_number, WireType::Fixed64, buf);
462            buf.extend_from_slice(&(*v as f64).to_le_bytes());
463        }
464        (FieldType::String, FieldValue::Str(s)) => {
465            let data = s.as_bytes();
466            encode_field(field_number, WireType::LengthDelimited, buf);
467            encode_varint(data.len() as u64, buf);
468            buf.extend_from_slice(data);
469        }
470        (FieldType::Bytes, FieldValue::Bytes(data)) => {
471            encode_field(field_number, WireType::LengthDelimited, buf);
472            encode_varint(data.len() as u64, buf);
473            buf.extend_from_slice(data);
474        }
475        (FieldType::Message(_), FieldValue::Message(data)) => {
476            encode_field(field_number, WireType::LengthDelimited, buf);
477            encode_varint(data.len() as u64, buf);
478            buf.extend_from_slice(data);
479        }
480        (FieldType::Repeated(_), FieldValue::Bytes(data)) => {
481            // Pre-encoded repeated/packed field
482            encode_field(field_number, WireType::LengthDelimited, buf);
483            encode_varint(data.len() as u64, buf);
484            buf.extend_from_slice(data);
485        }
486        _ => {
487            // Type mismatch — silently skip (caller should validate first)
488        }
489    }
490}
491
492fn wire_value_to_field_value(ft: &FieldType, wv: WireValue) -> SchemaRegistryResult<FieldValue> {
493    match (ft, wv) {
494        (FieldType::Int32, WireValue::Varint(v)) => Ok(FieldValue::Int32(v as i32)),
495        (FieldType::Int64, WireValue::Varint(v)) => Ok(FieldValue::Int64(v as i64)),
496        (FieldType::UInt32, WireValue::Varint(v)) => Ok(FieldValue::UInt32(v as u32)),
497        (FieldType::UInt64, WireValue::Varint(v)) => Ok(FieldValue::UInt64(v)),
498        (FieldType::Bool, WireValue::Varint(v)) => Ok(FieldValue::Bool(v != 0)),
499        (FieldType::Float, WireValue::Fixed32(b)) => Ok(FieldValue::Float(f32::from_le_bytes(b))),
500        (FieldType::Double, WireValue::Fixed64(b)) => Ok(FieldValue::Double(f64::from_le_bytes(b))),
501        (FieldType::String, WireValue::LengthDelimited(data)) => {
502            let s = std::string::String::from_utf8(data).map_err(|e| {
503                SchemaRegistryError::WireFormat(format!("invalid UTF-8 in string field: {e}"))
504            })?;
505            Ok(FieldValue::Str(s))
506        }
507        (FieldType::Bytes, WireValue::LengthDelimited(data)) => Ok(FieldValue::Bytes(data)),
508        (FieldType::Message(_), WireValue::LengthDelimited(data)) => Ok(FieldValue::Message(data)),
509        (FieldType::Repeated(_), WireValue::LengthDelimited(data)) => Ok(FieldValue::Bytes(data)),
510        (ft, wv) => Err(SchemaRegistryError::WireFormat(format!(
511            "wire type mismatch for field type {}: got {:?}",
512            ft.proto_name(),
513            wv
514        ))),
515    }
516}
517
518// ─── Tests ───────────────────────────────────────────────────────────────────
519
520#[cfg(test)]
521mod tests {
522    use super::*;
523
524    // ── Varint ────────────────────────────────────────────────────────────────
525
526    #[test]
527    fn test_varint_zero() {
528        let mut buf = Vec::new();
529        encode_varint(0, &mut buf);
530        assert_eq!(buf, [0x00]);
531        let mut pos = 0;
532        assert_eq!(decode_varint(&buf, &mut pos), Some(0));
533        assert_eq!(pos, 1);
534    }
535
536    #[test]
537    fn test_varint_one_byte_boundary() {
538        let mut buf = Vec::new();
539        encode_varint(127, &mut buf);
540        assert_eq!(buf, [0x7f]);
541    }
542
543    #[test]
544    fn test_varint_two_byte_boundary() {
545        let mut buf = Vec::new();
546        encode_varint(128, &mut buf);
547        assert_eq!(buf, [0x80, 0x01]);
548    }
549
550    #[test]
551    fn test_varint_300() {
552        let mut buf = Vec::new();
553        encode_varint(300, &mut buf);
554        assert_eq!(buf, [0xac, 0x02]);
555        let mut pos = 0;
556        assert_eq!(decode_varint(&buf, &mut pos), Some(300));
557    }
558
559    #[test]
560    fn test_varint_u64_max() {
561        let mut buf = Vec::new();
562        encode_varint(u64::MAX, &mut buf);
563        assert_eq!(buf.len(), 10);
564        let mut pos = 0;
565        assert_eq!(decode_varint(&buf, &mut pos), Some(u64::MAX));
566    }
567
568    #[test]
569    fn test_varint_roundtrip_sequence() {
570        let values: &[u64] = &[0, 1, 127, 128, 255, 1024, 65535, 1 << 32, u64::MAX];
571        for &v in values {
572            let mut buf = Vec::new();
573            encode_varint(v, &mut buf);
574            let mut pos = 0;
575            assert_eq!(decode_varint(&buf, &mut pos), Some(v), "value={v}");
576        }
577    }
578
579    #[test]
580    fn test_decode_varint_truncated_returns_none() {
581        // A multi-byte varint that is cut short
582        let buf = [0x80]; // continuation bit set but no following byte
583        let mut pos = 0;
584        assert_eq!(decode_varint(&buf, &mut pos), None);
585    }
586
587    // ── Field tag encoding ────────────────────────────────────────────────────
588
589    #[test]
590    fn test_wire_type_tag_field_1_varint() {
591        // field 1, wire type 0 → tag = (1 << 3) | 0 = 8
592        let mut buf = Vec::new();
593        encode_field(1, WireType::Varint, &mut buf);
594        assert_eq!(buf, [0x08]);
595    }
596
597    #[test]
598    fn test_wire_type_tag_field_2_len_delim() {
599        // field 2, wire type 2 → tag = (2 << 3) | 2 = 18
600        let mut buf = Vec::new();
601        encode_field(2, WireType::LengthDelimited, &mut buf);
602        assert_eq!(buf, [0x12]);
603    }
604
605    // ── ProtoEncoder ─────────────────────────────────────────────────────────
606
607    #[test]
608    fn test_proto_encoder_int32() {
609        let bytes = ProtoEncoder::new().int32(1, 150).build();
610        // tag = 0x08, varint 150 = [0x96, 0x01]
611        assert_eq!(bytes, [0x08, 0x96, 0x01]);
612    }
613
614    #[test]
615    fn test_proto_encoder_string() {
616        let bytes = ProtoEncoder::new().string(1, "testing").build();
617        // tag = 0x0a (field 1, wire type 2), length = 7, then "testing"
618        assert_eq!(bytes[0], 0x0a);
619        assert_eq!(bytes[1], 7);
620        assert_eq!(&bytes[2..], b"testing");
621    }
622
623    #[test]
624    fn test_proto_encoder_bool_true() {
625        let bytes = ProtoEncoder::new().bool(1, true).build();
626        assert_eq!(bytes, [0x08, 0x01]);
627    }
628
629    #[test]
630    fn test_proto_encoder_bool_false() {
631        let bytes = ProtoEncoder::new().bool(1, false).build();
632        assert_eq!(bytes, [0x08, 0x00]);
633    }
634
635    #[test]
636    fn test_proto_encoder_float() {
637        let v = 1.0_f32;
638        let bytes = ProtoEncoder::new().float(1, v).build();
639        // tag field 1 wire type 5 = (1 << 3) | 5 = 0x0d
640        assert_eq!(bytes[0], 0x0d);
641        let decoded = f32::from_le_bytes(bytes[1..5].try_into().expect("slice"));
642        assert!((decoded - 1.0).abs() < 1e-6);
643    }
644
645    #[test]
646    fn test_proto_encoder_double() {
647        let v = std::f64::consts::PI;
648        let bytes = ProtoEncoder::new().double(1, v).build();
649        // tag field 1 wire type 1 = (1 << 3) | 1 = 0x09
650        assert_eq!(bytes[0], 0x09);
651        let decoded = f64::from_le_bytes(bytes[1..9].try_into().expect("slice"));
652        assert!((decoded - std::f64::consts::PI).abs() < 1e-12);
653    }
654
655    #[test]
656    fn test_proto_encoder_bytes() {
657        let data = b"\xde\xad\xbe\xef";
658        let bytes = ProtoEncoder::new().bytes(1, data).build();
659        assert_eq!(bytes[0], 0x0a); // field 1, wire type 2
660        assert_eq!(bytes[1], 4); // length
661        assert_eq!(&bytes[2..], data);
662    }
663
664    #[test]
665    fn test_proto_encoder_message_nested() {
666        let inner = ProtoEncoder::new().int32(1, 42).build();
667        let outer = ProtoEncoder::new().message(1, &inner).build();
668        // The outer message wraps the inner as a length-delimited field
669        let mut dec = ProtoDecoder::new(&outer);
670        let (fn_, wv) = dec.next_field().expect("field").expect("ok");
671        assert_eq!(fn_, 1);
672        if let WireValue::LengthDelimited(payload) = wv {
673            assert_eq!(payload, inner);
674        } else {
675            panic!("expected LengthDelimited");
676        }
677    }
678
679    // ── ProtoDecoder ─────────────────────────────────────────────────────────
680
681    #[test]
682    fn test_proto_decoder_varint_field() {
683        let bytes = ProtoEncoder::new().int64(3, 9999).build();
684        let mut dec = ProtoDecoder::new(&bytes);
685        let (fn_, wv) = dec.next_field().expect("field").expect("ok");
686        assert_eq!(fn_, 3);
687        assert_eq!(wv, WireValue::Varint(9999));
688        assert!(dec.is_empty());
689    }
690
691    #[test]
692    fn test_proto_decoder_multiple_fields() {
693        let bytes = ProtoEncoder::new()
694            .int32(1, 1)
695            .string(2, "abc")
696            .bool(3, true)
697            .build();
698
699        let mut dec = ProtoDecoder::new(&bytes);
700        let fields = dec.collect_all().expect("ok");
701        assert_eq!(fields.len(), 3);
702        assert_eq!(fields[0].0, 1);
703        assert_eq!(fields[1].0, 2);
704        assert_eq!(fields[2].0, 3);
705    }
706
707    // ── Schema-guided encode/decode ───────────────────────────────────────────
708
709    #[test]
710    fn test_encode_decode_all_field_types() {
711        use crate::schema_registry::types::FieldDescriptor;
712
713        let desc = MessageDescriptor::new("AllTypes", "test")
714            .with_field(FieldDescriptor::optional(1, "i32", FieldType::Int32))
715            .with_field(FieldDescriptor::optional(2, "i64", FieldType::Int64))
716            .with_field(FieldDescriptor::optional(3, "u32", FieldType::UInt32))
717            .with_field(FieldDescriptor::optional(4, "u64", FieldType::UInt64))
718            .with_field(FieldDescriptor::optional(5, "flt", FieldType::Float))
719            .with_field(FieldDescriptor::optional(6, "dbl", FieldType::Double))
720            .with_field(FieldDescriptor::optional(7, "b", FieldType::Bool))
721            .with_field(FieldDescriptor::optional(8, "s", FieldType::String))
722            .with_field(FieldDescriptor::optional(9, "raw", FieldType::Bytes));
723
724        let values: Vec<(u32, FieldValue)> = vec![
725            (1, FieldValue::Int32(-7)),
726            (2, FieldValue::Int64(-9_999_999_999)),
727            (3, FieldValue::UInt32(42)),
728            (4, FieldValue::UInt64(u64::MAX)),
729            (5, FieldValue::Float(3.25)),
730            (6, FieldValue::Double(2.345_678_901)),
731            (7, FieldValue::Bool(true)),
732            (8, FieldValue::Str("hello".to_string())),
733            (9, FieldValue::Bytes(vec![0xca, 0xfe])),
734        ];
735
736        let bytes = encode_message(&desc, &values);
737        let decoded = decode_message(&desc, &bytes).expect("decode ok");
738        assert_eq!(decoded.len(), 9);
739
740        assert_eq!(decoded[0], ("i32".to_string(), FieldValue::Int32(-7)));
741        assert_eq!(
742            decoded[1],
743            ("i64".to_string(), FieldValue::Int64(-9_999_999_999))
744        );
745        assert_eq!(decoded[2], ("u32".to_string(), FieldValue::UInt32(42)));
746        assert_eq!(
747            decoded[3],
748            ("u64".to_string(), FieldValue::UInt64(u64::MAX))
749        );
750        assert_eq!(decoded[6], ("b".to_string(), FieldValue::Bool(true)));
751        assert_eq!(
752            decoded[7],
753            ("s".to_string(), FieldValue::Str("hello".to_string()))
754        );
755        assert_eq!(
756            decoded[8],
757            ("raw".to_string(), FieldValue::Bytes(vec![0xca, 0xfe]))
758        );
759    }
760
761    #[test]
762    fn test_message_encode_decode_roundtrip() {
763        use crate::schema_registry::types::FieldDescriptor;
764
765        let desc = MessageDescriptor::new("Point", "geometry")
766            .with_field(FieldDescriptor::optional(1, "x", FieldType::Double))
767            .with_field(FieldDescriptor::optional(2, "y", FieldType::Double))
768            .with_field(FieldDescriptor::optional(3, "label", FieldType::String));
769
770        let values = vec![
771            (1, FieldValue::Double(1.5)),
772            (2, FieldValue::Double(-3.75)),
773            (3, FieldValue::Str("origin".to_string())),
774        ];
775
776        let encoded = encode_message(&desc, &values);
777        let decoded = decode_message(&desc, &encoded).expect("decode ok");
778
779        assert_eq!(decoded.len(), 3);
780        assert_eq!(decoded[2].1, FieldValue::Str("origin".to_string()));
781    }
782
783    #[test]
784    fn test_nested_message_encoding() {
785        use crate::schema_registry::types::FieldDescriptor;
786
787        // Inner: { id: int32 }
788        let inner_desc = MessageDescriptor::new("Inner", "test")
789            .with_field(FieldDescriptor::optional(1, "id", FieldType::Int32));
790
791        let inner_bytes = encode_message(&inner_desc, &[(1, FieldValue::Int32(99))]);
792
793        // Outer: { nested: message(Inner) }
794        let outer_desc = MessageDescriptor::new("Outer", "test").with_field(
795            FieldDescriptor::optional(1, "nested", FieldType::Message("Inner".to_string())),
796        );
797
798        let outer_values = vec![(1, FieldValue::Message(inner_bytes.clone()))];
799        let outer_bytes = encode_message(&outer_desc, &outer_values);
800        let outer_decoded = decode_message(&outer_desc, &outer_bytes).expect("ok");
801
802        assert_eq!(outer_decoded.len(), 1);
803        if let FieldValue::Message(payload) = &outer_decoded[0].1 {
804            assert_eq!(payload, &inner_bytes);
805        } else {
806            panic!("expected Message variant");
807        }
808    }
809
810    #[test]
811    fn test_repeated_field_encoding() {
812        use crate::schema_registry::types::FieldDescriptor;
813
814        // Repeated int32: packed encoding — client pre-packs as bytes
815        let desc = MessageDescriptor::new("Bag", "test").with_field(FieldDescriptor::optional(
816            1,
817            "items",
818            FieldType::Repeated(Box::new(FieldType::Int32)),
819        ));
820
821        // Pack [1, 2, 3] as varints
822        let mut packed = Vec::new();
823        for v in [1u64, 2, 3] {
824            encode_varint(v, &mut packed);
825        }
826
827        let values = vec![(1, FieldValue::Bytes(packed.clone()))];
828        let bytes = encode_message(&desc, &values);
829        let decoded = decode_message(&desc, &bytes).expect("ok");
830
831        assert_eq!(decoded.len(), 1);
832        assert_eq!(decoded[0].0, "items");
833        if let FieldValue::Bytes(b) = &decoded[0].1 {
834            assert_eq!(b, &packed);
835        } else {
836            panic!("expected Bytes");
837        }
838    }
839
840    #[test]
841    fn test_unknown_field_skipped_on_decode() {
842        use crate::schema_registry::types::FieldDescriptor;
843
844        // Encode a message with two fields
845        let desc_full = MessageDescriptor::new("M", "test")
846            .with_field(FieldDescriptor::optional(1, "a", FieldType::Int32))
847            .with_field(FieldDescriptor::optional(2, "b", FieldType::String));
848
849        let bytes = encode_message(
850            &desc_full,
851            &[
852                (1, FieldValue::Int32(7)),
853                (2, FieldValue::Str("x".to_string())),
854            ],
855        );
856
857        // Decode with a schema that only knows about field 1
858        let desc_partial = MessageDescriptor::new("M", "test")
859            .with_field(FieldDescriptor::optional(1, "a", FieldType::Int32));
860
861        let decoded = decode_message(&desc_partial, &bytes).expect("ok");
862        // Field 2 should be silently skipped
863        assert_eq!(decoded.len(), 1);
864        assert_eq!(decoded[0].1, FieldValue::Int32(7));
865    }
866}