@@ -175,7 +175,76 @@ impl<const N: usize> Encode for [u8; N] {
175
175
}
176
176
}
177
177
178
- /// Encode a `string` as described in [RFC4251 § 5]:
178
+ /// A macro to implement `Encode` for a type by delegating to some transformed version of `self`.
179
+ macro_rules! impl_by_delegation {
180
+ (
181
+ $(
182
+ $( #[ $attr: meta] ) *
183
+ impl $( ( $( $generics: tt) +) ) ? Encode for $type: ty where $self: ident -> $delegate: expr;
184
+ ) +
185
+ ) => {
186
+ $(
187
+ $( #[ $attr] ) *
188
+ impl $( < $( $generics) * >) ? Encode for $type {
189
+ fn encoded_len( & $self) -> Result <usize , Error > {
190
+ $delegate. encoded_len( )
191
+ }
192
+
193
+ fn encode( & $self, writer: & mut impl Writer ) -> Result <( ) , Error > {
194
+ $delegate. encode( writer)
195
+ }
196
+ }
197
+ ) +
198
+ } ;
199
+ }
200
+
201
+ impl_by_delegation ! (
202
+ /// Encode a `string` as described in [RFC4251 § 5]:
203
+ ///
204
+ /// > Arbitrary length binary string. Strings are allowed to contain
205
+ /// > arbitrary binary data, including null characters and 8-bit
206
+ /// > characters. They are stored as a uint32 containing its length
207
+ /// > (number of bytes that follow) and zero (= empty string) or more
208
+ /// > bytes that are the value of the string. Terminating null
209
+ /// > characters are not used.
210
+ /// >
211
+ /// > Strings are also used to store text. In that case, US-ASCII is
212
+ /// > used for internal names, and ISO-10646 UTF-8 for text that might
213
+ /// > be displayed to the user. The terminating null character SHOULD
214
+ /// > NOT normally be stored in the string. For example: the US-ASCII
215
+ /// > string "testing" is represented as 00 00 00 07 t e s t i n g. The
216
+ /// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
217
+ ///
218
+ /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
219
+ impl Encode for str where self -> self . as_bytes( ) ;
220
+
221
+ #[ cfg( feature = "alloc" ) ]
222
+ impl Encode for Vec <u8 > where self -> self . as_slice( ) ;
223
+ #[ cfg( feature = "alloc" ) ]
224
+ impl Encode for String where self -> self . as_bytes( ) ;
225
+ #[ cfg( feature = "bytes" ) ]
226
+ impl Encode for Bytes where self -> self . as_ref( ) ;
227
+
228
+ // While deref coercion ensures that `&E` can use the `Encode` trait methods, it will not be
229
+ // allowd in trait bounds, as `&E` does not implement `Encode` itself just because `E: Encode`.
230
+ // A blanket impl for `&E` would be the most generic, but that collides with the `Label` trait's
231
+ // blanket impl. Instead, we can do it explicitly for the immediatley relevant base types.
232
+ impl Encode for & str where self -> * * self ;
233
+ impl Encode for & [ u8 ] where self -> * * self ;
234
+ #[ cfg( feature = "alloc" ) ]
235
+ impl Encode for & Vec <u8 > where self -> * * self ;
236
+ #[ cfg( feature = "alloc" ) ]
237
+ impl Encode for & String where self -> * * self ;
238
+ #[ cfg( feature = "bytes" ) ]
239
+ impl Encode for & Bytes where self -> * * self ;
240
+
241
+ ) ;
242
+
243
+ /// A trait indicating that the type is encoded like an RFC4251 string.
244
+ ///
245
+ /// Implementing this trait allows encoding sequences of the type as a string of strings.
246
+ ///
247
+ /// A `string` is described in [RFC4251 § 5]:
179
248
///
180
249
/// > Arbitrary length binary string. Strings are allowed to contain
181
250
/// > arbitrary binary data, including null characters and 8-bit
@@ -192,40 +261,27 @@ impl<const N: usize> Encode for [u8; N] {
192
261
/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
193
262
///
194
263
/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
195
- impl Encode for & str {
196
- fn encoded_len ( & self ) -> Result < usize , Error > {
197
- self . as_bytes ( ) . encoded_len ( )
198
- }
199
-
200
- fn encode ( & self , writer : & mut impl Writer ) -> Result < ( ) , Error > {
201
- self . as_bytes ( ) . encode ( writer)
202
- }
203
- }
264
+ pub trait Rfc4251String : Encode { }
204
265
266
+ impl Rfc4251String for str { }
267
+ impl Rfc4251String for [ u8 ] { }
205
268
#[ cfg( feature = "alloc" ) ]
206
- impl Encode for Vec < u8 > {
207
- fn encoded_len ( & self ) -> Result < usize , Error > {
208
- self . as_slice ( ) . encoded_len ( )
209
- }
210
-
211
- fn encode ( & self , writer : & mut impl Writer ) -> Result < ( ) , Error > {
212
- self . as_slice ( ) . encode ( writer)
213
- }
214
- }
215
-
269
+ impl Rfc4251String for String { }
216
270
#[ cfg( feature = "alloc" ) ]
217
- impl Encode for String {
218
- fn encoded_len ( & self ) -> Result < usize , Error > {
219
- self . as_str ( ) . encoded_len ( )
220
- }
221
-
222
- fn encode ( & self , writer : & mut impl Writer ) -> Result < ( ) , Error > {
223
- self . as_str ( ) . encode ( writer)
224
- }
271
+ impl Rfc4251String for Vec < u8 > { }
272
+ #[ cfg( feature = "bytes" ) ]
273
+ impl Rfc4251String for Bytes { }
274
+
275
+ /// Any reference to [`Rfc4251String`] is itself [`Rfc4251String`] if `&T: Encode`.
276
+ impl < ' a , T > Rfc4251String for & ' a T
277
+ where
278
+ T : Rfc4251String + ?Sized ,
279
+ & ' a T : Encode ,
280
+ {
225
281
}
226
282
227
- # [ cfg ( feature = "alloc" ) ]
228
- impl Encode for Vec < String > {
283
+ /// Encode a slice of string-like types as a string wrapping all the entries.
284
+ impl < T : Rfc4251String > Encode for [ T ] {
229
285
fn encoded_len ( & self ) -> Result < usize , Error > {
230
286
self . iter ( ) . try_fold ( 4usize , |acc, string| {
231
287
acc. checked_add ( string. encoded_len ( ) ?) . ok_or ( Error :: Length )
@@ -237,22 +293,11 @@ impl Encode for Vec<String> {
237
293
. checked_sub ( 4 )
238
294
. ok_or ( Error :: Length ) ?
239
295
. encode ( writer) ?;
240
-
241
- for entry in self {
242
- entry. encode ( writer) ?;
243
- }
244
-
245
- Ok ( ( ) )
296
+ self . iter ( ) . try_fold ( ( ) , |( ) , entry| entry. encode ( writer) )
246
297
}
247
298
}
248
299
249
- #[ cfg( feature = "bytes" ) ]
250
- impl Encode for Bytes {
251
- fn encoded_len ( & self ) -> Result < usize , Error > {
252
- self . as_ref ( ) . encoded_len ( )
253
- }
254
-
255
- fn encode ( & self , writer : & mut impl Writer ) -> Result < ( ) , Error > {
256
- self . as_ref ( ) . encode ( writer)
257
- }
258
- }
300
+ impl_by_delegation ! (
301
+ #[ cfg( feature = "alloc" ) ]
302
+ impl ( T : Rfc4251String ) Encode for Vec <T > where self -> self . as_slice( ) ;
303
+ ) ;
0 commit comments