@@ -134,6 +134,82 @@ impl Decodable for CommandString {
134134 }
135135}
136136
137+ impl encoding:: Encodable for CommandString {
138+ type Encoder < ' e > = encoding:: ArrayEncoder < 12 > ;
139+
140+ fn encoder ( & self ) -> Self :: Encoder < ' _ > {
141+ let mut rawbytes = [ 0u8 ; 12 ] ;
142+ let strbytes = self . 0 . as_bytes ( ) ;
143+ debug_assert ! ( strbytes. len( ) <= 12 ) ;
144+ rawbytes[ ..strbytes. len ( ) ] . copy_from_slice ( strbytes) ;
145+ encoding:: ArrayEncoder :: without_length_prefix ( rawbytes)
146+ }
147+ }
148+
149+ impl encoding:: Decodable for CommandString {
150+ type Decoder = CommandStringDecoder ;
151+
152+ fn decoder ( ) -> Self :: Decoder { CommandStringDecoder { inner : encoding:: ArrayDecoder :: new ( ) } }
153+ }
154+
155+ /// Decoder for [`CommandString`].
156+ pub struct CommandStringDecoder {
157+ inner : encoding:: ArrayDecoder < 12 > ,
158+ }
159+
160+ impl encoding:: Decoder for CommandStringDecoder {
161+ type Output = CommandString ;
162+ type Error = CommandStringDecodeError ;
163+
164+ fn push_bytes ( & mut self , bytes : & mut & [ u8 ] ) -> Result < bool , Self :: Error > {
165+ self . inner . push_bytes ( bytes) . map_err ( CommandStringDecodeError :: UnexpectedEof )
166+ }
167+
168+ fn end ( self ) -> Result < Self :: Output , Self :: Error > {
169+ let rawbytes = self . inner . end ( ) . map_err ( CommandStringDecodeError :: UnexpectedEof ) ?;
170+ // Trim null padding from the end.
171+ let trimmed =
172+ rawbytes. iter ( ) . rposition ( |& b| b != 0 ) . map_or ( & rawbytes[ ..0 ] , |i| & rawbytes[ ..=i] ) ;
173+
174+ if !trimmed. is_ascii ( ) {
175+ return Err ( CommandStringDecodeError :: NotAscii ) ;
176+ }
177+
178+ Ok ( CommandString ( Cow :: Owned ( unsafe { String :: from_utf8_unchecked ( trimmed. to_vec ( ) ) } ) ) )
179+ }
180+
181+ fn read_limit ( & self ) -> usize { self . inner . read_limit ( ) }
182+ }
183+
184+ /// Error decoding a [`CommandString`].
185+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
186+ #[ non_exhaustive]
187+ pub enum CommandStringDecodeError {
188+ /// Unexpected end of data.
189+ UnexpectedEof ( encoding:: UnexpectedEofError ) ,
190+ /// Command string contains non-ASCII characters.
191+ NotAscii ,
192+ }
193+
194+ impl fmt:: Display for CommandStringDecodeError {
195+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
196+ match self {
197+ Self :: UnexpectedEof ( e) => write ! ( f, "unexpected end of data: {}" , e) ,
198+ Self :: NotAscii => write ! ( f, "command string must be ASCII" ) ,
199+ }
200+ }
201+ }
202+
203+ #[ cfg( feature = "std" ) ]
204+ impl std:: error:: Error for CommandStringDecodeError {
205+ fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
206+ match self {
207+ Self :: UnexpectedEof ( e) => Some ( e) ,
208+ Self :: NotAscii => None ,
209+ }
210+ }
211+ }
212+
137213/// Error returned when a command string is invalid.
138214///
139215/// This is currently returned for command strings longer than 12.
0 commit comments