1use buffa::{
18 encoding::{Tag, WireType},
19 types,
20};
21use bytes::BytesMut;
22use secrecy::{ExposeSecret, SecretBox, SecretString};
23
24pub trait BuffaEncodeField {
32 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut);
37}
38
39impl BuffaEncodeField for u32 {
42 #[inline]
43 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
44 if *self != 0 {
45 Tag::new(number, WireType::Varint).encode(buf);
46 types::encode_uint32(*self, buf);
47 }
48 }
49}
50
51impl BuffaEncodeField for u64 {
52 #[inline]
53 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
54 if *self != 0 {
55 Tag::new(number, WireType::Varint).encode(buf);
56 types::encode_uint64(*self, buf);
57 }
58 }
59}
60
61impl BuffaEncodeField for i32 {
62 #[inline]
63 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
64 if *self != 0 {
65 Tag::new(number, WireType::Varint).encode(buf);
66 types::encode_int32(*self, buf);
67 }
68 }
69}
70
71impl BuffaEncodeField for i64 {
72 #[inline]
73 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
74 if *self != 0 {
75 Tag::new(number, WireType::Varint).encode(buf);
76 types::encode_int64(*self, buf);
77 }
78 }
79}
80
81impl BuffaEncodeField for bool {
82 #[inline]
83 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
84 if *self {
85 Tag::new(number, WireType::Varint).encode(buf);
86 types::encode_bool(*self, buf);
87 }
88 }
89}
90
91impl BuffaEncodeField for f32 {
94 #[inline]
95 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
96 if *self != 0.0 {
97 Tag::new(number, WireType::Fixed32).encode(buf);
98 types::encode_float(*self, buf);
99 }
100 }
101}
102
103impl BuffaEncodeField for f64 {
104 #[inline]
105 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
106 if *self != 0.0 {
107 Tag::new(number, WireType::Fixed64).encode(buf);
108 types::encode_double(*self, buf);
109 }
110 }
111}
112
113impl BuffaEncodeField for String {
116 #[inline]
117 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
118 if !self.is_empty() {
119 Tag::new(number, WireType::LengthDelimited).encode(buf);
120 types::encode_string(self, buf);
121 }
122 }
123}
124
125impl BuffaEncodeField for &str {
126 #[inline]
127 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
128 if !self.is_empty() {
129 Tag::new(number, WireType::LengthDelimited).encode(buf);
130 types::encode_string(self, buf);
131 }
132 }
133}
134
135impl BuffaEncodeField for Vec<u8> {
136 #[inline]
137 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
138 if !self.is_empty() {
139 Tag::new(number, WireType::LengthDelimited).encode(buf);
140 types::encode_bytes(self, buf);
141 }
142 }
143}
144
145impl BuffaEncodeField for &[u8] {
146 #[inline]
147 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
148 if !self.is_empty() {
149 Tag::new(number, WireType::LengthDelimited).encode(buf);
150 types::encode_bytes(self, buf);
151 }
152 }
153}
154
155impl BuffaEncodeField for SecretString {
170 #[inline]
171 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
172 let exposed = self.expose_secret();
173 if !exposed.is_empty() {
174 Tag::new(number, WireType::LengthDelimited).encode(buf);
175 types::encode_string(exposed, buf);
176 }
177 }
178}
179
180impl BuffaEncodeField for SecretBox<Vec<u8>> {
181 #[inline]
182 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
183 let exposed = self.expose_secret();
184 if !exposed.is_empty() {
185 Tag::new(number, WireType::LengthDelimited).encode(buf);
186 types::encode_bytes(exposed, buf);
187 }
188 }
189}
190
191impl<T: BuffaEncodeField> BuffaEncodeField for Option<T> {
198 #[inline]
199 fn buffa_encode_field(&self, number: u32, buf: &mut BytesMut) {
200 if let Some(v) = self {
201 v.buffa_encode_field(number, buf);
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
212 fn test_should_skip_default_string() {
213 let mut buf = BytesMut::new();
214 String::new().buffa_encode_field(1, &mut buf);
215 assert!(buf.is_empty());
216 }
217
218 #[test]
219 fn test_should_emit_nonempty_string() {
220 let mut buf = BytesMut::new();
221 "hello".to_string().buffa_encode_field(1, &mut buf);
222 assert_eq!(&buf[..], &[0x0A, 0x05, b'h', b'e', b'l', b'l', b'o']);
224 }
225
226 #[test]
227 fn test_should_skip_default_uint64() {
228 let mut buf = BytesMut::new();
229 0u64.buffa_encode_field(2, &mut buf);
230 assert!(buf.is_empty());
231 }
232
233 #[test]
234 fn test_should_emit_nonzero_uint64() {
235 let mut buf = BytesMut::new();
236 42u64.buffa_encode_field(2, &mut buf);
237 assert_eq!(&buf[..], &[0x10, 0x2A]);
239 }
240
241 #[test]
242 fn test_should_emit_secret_string_via_expose() {
243 let mut buf = BytesMut::new();
244 let secret = SecretString::from("topsecret");
245 secret.buffa_encode_field(3, &mut buf);
246 assert!(buf.starts_with(&[0x1A, 0x09]));
249 assert!(buf.ends_with(b"topsecret"));
250 }
251
252 #[test]
253 fn test_secret_string_debug_should_be_redacted() {
254 let secret = SecretString::from("topsecret");
255 let dbg = format!("{secret:?}");
256 assert!(!dbg.contains("topsecret"));
257 assert!(dbg.to_ascii_lowercase().contains("redacted"));
258 }
259
260 #[test]
261 fn test_should_skip_none_option() {
262 let mut buf = BytesMut::new();
263 let v: Option<String> = None;
264 v.buffa_encode_field(1, &mut buf);
265 assert!(buf.is_empty());
266 }
267}