Skip to content

Commit e32c9d5

Browse files
committed
Introduce EncodesLikeString for types that encode like SSH strings
This is in turn used to implement `Encode` for slices of string-like types. Downstream users could implement the marker trait for their own types and get the same behavior for slices of their own types. Closes #314.
1 parent aac3aee commit e32c9d5

File tree

1 file changed

+44
-19
lines changed

1 file changed

+44
-19
lines changed

ssh-encoding/src/encode.rs

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ impl<const N: usize> Encode for [u8; N] {
192192
/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
193193
///
194194
/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
195-
impl Encode for &str {
195+
impl Encode for str {
196196
fn encoded_len(&self) -> Result<usize, Error> {
197197
self.as_bytes().encoded_len()
198198
}
@@ -224,35 +224,60 @@ impl Encode for String {
224224
}
225225
}
226226

227-
#[cfg(feature = "alloc")]
228-
impl Encode for Vec<String> {
227+
#[cfg(feature = "bytes")]
228+
impl Encode for Bytes {
229229
fn encoded_len(&self) -> Result<usize, Error> {
230-
self.iter().try_fold(4usize, |acc, string| {
231-
acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
232-
})
230+
self.as_ref().encoded_len()
233231
}
234232

235233
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
236-
self.encoded_len()?
237-
.checked_sub(4)
238-
.ok_or(Error::Length)?
239-
.encode(writer)?;
240-
241-
for entry in self {
242-
entry.encode(writer)?;
243-
}
244-
245-
Ok(())
234+
self.as_ref().encode(writer)
246235
}
247236
}
248237

238+
/// A trait indicating that the type is encoded like an SSH-string.
239+
///
240+
/// A `string` is described in [RFC4251 § 5]:
241+
///
242+
/// > Arbitrary length binary string. Strings are allowed to contain
243+
/// > arbitrary binary data, including null characters and 8-bit
244+
/// > characters. They are stored as a uint32 containing its length
245+
/// > (number of bytes that follow) and zero (= empty string) or more
246+
/// > bytes that are the value of the string. Terminating null
247+
/// > characters are not used.
248+
/// >
249+
/// > Strings are also used to store text. In that case, US-ASCII is
250+
/// > used for internal names, and ISO-10646 UTF-8 for text that might
251+
/// > be displayed to the user. The terminating null character SHOULD
252+
/// > NOT normally be stored in the string. For example: the US-ASCII
253+
/// > string "testing" is represented as 00 00 00 07 t e s t i n g. The
254+
/// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
255+
///
256+
/// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
257+
pub trait EncodesLikeString: Encode {}
258+
259+
impl EncodesLikeString for [u8] {}
260+
impl EncodesLikeString for str {}
261+
#[cfg(feature = "alloc")]
262+
impl EncodesLikeString for String {}
263+
#[cfg(feature = "alloc")]
264+
impl EncodesLikeString for Vec<u8> {}
249265
#[cfg(feature = "bytes")]
250-
impl Encode for Bytes {
266+
impl EncodesLikeString for Bytes {}
267+
268+
/// Encode a slice of string-like types as a string wrapping all the entires.
269+
impl<T: EncodesLikeString> Encode for [T] {
251270
fn encoded_len(&self) -> Result<usize, Error> {
252-
self.as_ref().encoded_len()
271+
self.iter().try_fold(4usize, |acc, string| {
272+
acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
273+
})
253274
}
254275

255276
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
256-
self.as_ref().encode(writer)
277+
self.encoded_len()?
278+
.checked_sub(4)
279+
.ok_or(Error::Length)?
280+
.encode(writer)?;
281+
self.iter().try_fold((), |(), entry| entry.encode(writer))
257282
}
258283
}

0 commit comments

Comments
 (0)