use buffa::{
encoding::{Tag, WireType},
types,
};
use bytes::BytesMut;
use secrecy::{ExposeSecret, SecretBox, SecretString};
pub trait BuffaEncodeField {
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut);
}
impl BuffaEncodeField for u32 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0 {
Tag::new(number, WireType::Varint).encode(buf);
types::encode_uint32(*self, buf);
}
}
}
impl BuffaEncodeField for u64 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0 {
Tag::new(number, WireType::Varint).encode(buf);
types::encode_uint64(*self, buf);
}
}
}
impl BuffaEncodeField for i32 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0 {
Tag::new(number, WireType::Varint).encode(buf);
types::encode_int32(*self, buf);
}
}
}
impl BuffaEncodeField for i64 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0 {
Tag::new(number, WireType::Varint).encode(buf);
types::encode_int64(*self, buf);
}
}
}
impl BuffaEncodeField for bool {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self {
Tag::new(number, WireType::Varint).encode(buf);
types::encode_bool(*self, buf);
}
}
}
impl BuffaEncodeField for f32 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0.0 {
Tag::new(number, WireType::Fixed32).encode(buf);
types::encode_float(*self, buf);
}
}
}
impl BuffaEncodeField for f64 {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if *self != 0.0 {
Tag::new(number, WireType::Fixed64).encode(buf);
types::encode_double(*self, buf);
}
}
}
impl BuffaEncodeField for String {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if !self.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_string(self, buf);
}
}
}
impl BuffaEncodeField for &str {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if !self.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_string(self, buf);
}
}
}
impl BuffaEncodeField for Vec<u8> {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if !self.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_bytes(self, buf);
}
}
}
impl BuffaEncodeField for &[u8] {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if !self.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_bytes(self, buf);
}
}
}
impl BuffaEncodeField for SecretString {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
let exposed = self.expose_secret();
if !exposed.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_string(exposed, buf);
}
}
}
impl BuffaEncodeField for SecretBox<Vec<u8>> {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
let exposed = self.expose_secret();
if !exposed.is_empty() {
Tag::new(number, WireType::LengthDelimited).encode(buf);
types::encode_bytes(exposed, buf);
}
}
}
impl<T: BuffaEncodeField> BuffaEncodeField for Option<T> {
#[inline]
fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
if let Some(v) = self {
v.buffa_encode_field(number, buf);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_skip_default_string() {
let mut buf = BytesMut::new();
String::new().buffa_encode_field(1, &mut buf);
assert!(buf.is_empty());
}
#[test]
fn test_should_emit_nonempty_string() {
let mut buf = BytesMut::new();
"hello".to_string().buffa_encode_field(1, &mut buf);
assert_eq!(&buf[..], &[0x0A, 0x05, b'h', b'e', b'l', b'l', b'o']);
}
#[test]
fn test_should_skip_default_uint64() {
let mut buf = BytesMut::new();
0u64.buffa_encode_field(2, &mut buf);
assert!(buf.is_empty());
}
#[test]
fn test_should_emit_nonzero_uint64() {
let mut buf = BytesMut::new();
42u64.buffa_encode_field(2, &mut buf);
assert_eq!(&buf[..], &[0x10, 0x2A]);
}
#[test]
fn test_should_emit_secret_string_via_expose() {
let mut buf = BytesMut::new();
let secret = SecretString::from("topsecret");
secret.buffa_encode_field(3, &mut buf);
assert!(buf.starts_with(&[0x1A, 0x09]));
assert!(buf.ends_with(b"topsecret"));
}
#[test]
fn test_secret_string_debug_should_be_redacted() {
let secret = SecretString::from("topsecret");
let dbg = format!("{secret:?}");
assert!(!dbg.contains("topsecret"));
assert!(dbg.to_ascii_lowercase().contains("redacted"));
}
#[test]
fn test_should_skip_none_option() {
let mut buf = BytesMut::new();
let v: Option<String> = None;
v.buffa_encode_field(1, &mut buf);
assert!(buf.is_empty());
}
}