@@ -22,7 +22,7 @@ use crate::{
22
22
use std:: {
23
23
char,
24
24
cmp:: { min, Ordering } ,
25
- io:: { stdin, stdout, Read , Stdin , Stdout , Write } ,
25
+ io:: { stdin, stdout, Read , Stdout , Write } ,
26
26
panic,
27
27
sync:: mpsc:: Sender ,
28
28
thread:: { spawn, JoinHandle } ,
@@ -748,16 +748,30 @@ fn spawn_input_thread(tx: Sender<Event>) -> JoinHandle<()> {
748
748
} )
749
749
}
750
750
751
- fn try_read_char ( stdin : & mut Stdin ) -> Option < char > {
752
- let mut buf: [ u8 ; 1 ] = [ 0 ; 1 ] ;
753
- if stdin. read_exact ( & mut buf) . is_ok ( ) {
754
- Some ( buf[ 0 ] as char )
755
- } else {
756
- None
751
+ fn try_read_char ( stdin : & mut impl Read ) -> Option < char > {
752
+ let mut buf: [ u8 ; 4 ] = [ 0 ; 4 ] ;
753
+
754
+ for i in 0 ..4 {
755
+ if stdin. read_exact ( & mut buf[ i..i + 1 ] ) . is_err ( ) {
756
+ return if i == 0 {
757
+ None
758
+ } else {
759
+ Some ( char:: REPLACEMENT_CHARACTER )
760
+ } ;
761
+ }
762
+
763
+ match std:: str:: from_utf8 ( & buf[ 0 ..i + 1 ] ) {
764
+ Ok ( s) => return s. chars ( ) . next ( ) ,
765
+ Err ( e) if e. error_len ( ) . is_some ( ) => return Some ( char:: REPLACEMENT_CHARACTER ) ,
766
+ Err ( _) => ( ) ,
767
+ }
757
768
}
769
+
770
+ // utf8 requires at most 4 bytes so at this point we have invalid data
771
+ Some ( char:: REPLACEMENT_CHARACTER )
758
772
}
759
773
760
- fn try_read_input ( stdin : & mut Stdin ) -> Option < Input > {
774
+ fn try_read_input ( stdin : & mut impl Read ) -> Option < Input > {
761
775
let c = try_read_char ( stdin) ?;
762
776
763
777
// Normal key press
@@ -811,3 +825,26 @@ fn try_read_input(stdin: &mut Stdin) -> Option<Input> {
811
825
812
826
Some ( Input :: Esc )
813
827
}
828
+
829
+ #[ cfg( test) ]
830
+ mod tests {
831
+ use super :: * ;
832
+ use simple_test_case:: test_case;
833
+ use std:: { char:: REPLACEMENT_CHARACTER , io} ;
834
+
835
+ #[ test_case( "a" . as_bytes( ) , & [ 'a' ] ; "single ascii character" ) ]
836
+ #[ test_case( & [ 240 , 159 , 146 , 150 ] , & [ '💖' ] ; "single utf8 character" ) ]
837
+ #[ test_case( & [ 165 , 159 , 146 , 150 ] , & [ REPLACEMENT_CHARACTER ; 4 ] ; "invalid utf8 with non-ascii prefix" ) ]
838
+ #[ test_case( & [ 65 , 159 , 146 , 150 ] , & [ 'A' , REPLACEMENT_CHARACTER , REPLACEMENT_CHARACTER , REPLACEMENT_CHARACTER ] ; "invalid utf8 with ascii prefix" ) ]
839
+ #[ test]
840
+ fn try_read_char_works ( bytes : & [ u8 ] , expected : & [ char ] ) {
841
+ let mut r = io:: Cursor :: new ( bytes) ;
842
+ let mut chars = Vec :: new ( ) ;
843
+
844
+ while let Some ( ch) = try_read_char ( & mut r) {
845
+ chars. push ( ch) ;
846
+ }
847
+
848
+ assert_eq ! ( & chars, expected) ;
849
+ }
850
+ }
0 commit comments