@@ -175,7 +175,76 @@ impl<const N: usize> Encode for [u8; N] {
175175 }
176176}
177177
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]:
179248///
180249/// > Arbitrary length binary string. Strings are allowed to contain
181250/// > arbitrary binary data, including null characters and 8-bit
@@ -192,40 +261,27 @@ impl<const N: usize> Encode for [u8; N] {
192261/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
193262///
194263/// [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 { }
204265
266+ impl Rfc4251String for str { }
267+ impl Rfc4251String for [ u8 ] { }
205268#[ 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 { }
216270#[ 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+ {
225281}
226282
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 ] {
229285 fn encoded_len ( & self ) -> Result < usize , Error > {
230286 self . iter ( ) . try_fold ( 4usize , |acc, string| {
231287 acc. checked_add ( string. encoded_len ( ) ?) . ok_or ( Error :: Length )
@@ -237,22 +293,11 @@ impl Encode for Vec<String> {
237293 . checked_sub ( 4 )
238294 . ok_or ( Error :: Length ) ?
239295 . 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) )
246297 }
247298}
248299
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