Skip to content

Instantly share code, notes, and snippets.

@muhamadazmy
Created May 12, 2025 10:12
Show Gist options
  • Save muhamadazmy/e5973a1cd6d9849e3c9a168e00a2ec5d to your computer and use it in GitHub Desktop.
Save muhamadazmy/e5973a1cd6d9849e3c9a168e00a2ec5d to your computer and use it in GitHub Desktop.
Implement a custom encoder for a foreign type
use std::ops::RangeInclusive;
use bilrost::{
buf::ReverseBuf,
encoding::{
Capped, DecodeContext, Decoder, EmptyState, Encoder, ForOverwrite, General, TagMeasurer,
TagRevWriter, TagWriter, WireType, Wiretyped,
},
DecodeError, Message, OwnedMessage,
};
use bytes::Bytes;
#[derive(bilrost::Message)]
struct MyMessage {
#[bilrost(encoding(Range))]
range: RangeInclusive<u64>,
}
struct Range;
impl<T> Encoder<Range> for RangeInclusive<T>
where
T: Copy + Default,
(T, T): Encoder<General>,
{
fn encode<B: bytes::BufMut + ?Sized>(tag: u32, value: &Self, buf: &mut B, tw: &mut TagWriter) {
let v = (*value.start(), *value.end());
<(T, T) as Encoder<General>>::encode(tag, &v, buf, tw)
}
fn prepend_encode<B: ReverseBuf + ?Sized>(
tag: u32,
value: &Self,
buf: &mut B,
tw: &mut TagRevWriter,
) {
let v = (*value.start(), *value.end());
<(T, T) as Encoder<General>>::prepend_encode(tag, &v, buf, tw)
}
fn encoded_len(tag: u32, value: &Self, tm: &mut impl TagMeasurer) -> usize {
let v = (*value.start(), *value.end());
<(T, T) as Encoder<General>>::encoded_len(tag, &v, tm)
}
}
impl<T> Decoder<Range> for RangeInclusive<T>
where
T: Default + Copy,
(T, T): Decoder<General>,
{
fn decode<B: bytes::Buf + ?Sized>(
wire_type: WireType,
value: &mut Self,
buf: Capped<B>,
ctx: DecodeContext,
) -> Result<(), DecodeError> {
let mut v = (T::default(), T::default());
<(T, T) as Decoder<General>>::decode(wire_type, &mut v, buf, ctx)?;
*value = Self::new(v.0, v.1);
Ok(())
}
}
impl<T> Wiretyped<Range> for RangeInclusive<T>
where
(T, T): Wiretyped<General>,
{
const WIRE_TYPE: WireType = <(T, T) as Wiretyped<General>>::WIRE_TYPE;
}
impl<T> EmptyState<Range> for RangeInclusive<T>
where
T: Default + Copy + PartialEq,
{
fn clear(&mut self) {
*self = Self::new(T::default(), T::default());
}
fn is_empty(&self) -> bool {
self.start() == self.end()
}
fn empty() -> Self
where
Self: Sized,
{
Self::new(T::default(), T::default())
}
}
impl<T> ForOverwrite<Range> for RangeInclusive<T>
where
T: Default,
{
fn for_overwrite() -> Self
where
Self: Sized,
{
Self::new(T::default(), T::default())
}
}
fn main() {
let m = MyMessage { range: 1..=10 };
let v = m.encode_to_vec();
let m2 = <MyMessage as OwnedMessage>::decode(Bytes::from(v)).unwrap();
assert_eq!(m.range, m2.range);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment