@@ -610,17 +610,85 @@ pub struct Suggestion {
610610}
611611
612612#[ derive( Eq , PartialEq , Clone , Debug , Serialize , Deserialize ) ]
613+ #[ repr( u8 ) ]
613614pub enum MessageType {
614615 Error = 0 ,
615616 Diagnostic = 1 ,
616617}
617618
618- impl MessageType {
619- pub fn from_u8 ( value : u8 ) -> Option < Self > {
619+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
620+ pub struct InvalidMessageType ( pub u8 ) ;
621+
622+ impl std:: fmt:: Display for InvalidMessageType {
623+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
624+ write ! ( f, "invalid message type: {}" , self . 0 )
625+ }
626+ }
627+
628+ impl std:: error:: Error for InvalidMessageType { }
629+
630+ impl TryFrom < u8 > for MessageType {
631+ type Error = InvalidMessageType ;
632+
633+ fn try_from ( value : u8 ) -> Result < Self , InvalidMessageType > {
620634 match value {
621- 0 => Some ( MessageType :: Error ) ,
622- 1 => Some ( MessageType :: Diagnostic ) ,
623- _ => None ,
635+ 0 => Ok ( Self :: Error ) ,
636+ 1 => Ok ( Self :: Diagnostic ) ,
637+ _ => Err ( InvalidMessageType ( value) ) ,
638+ }
639+ }
640+ }
641+
642+ /// Iterator that streams messages from tsgolint stdout.
643+ struct TsGoLintMessageStream {
644+ stdout : std:: process:: ChildStdout ,
645+ buffer : Vec < u8 > ,
646+ }
647+
648+ impl TsGoLintMessageStream {
649+ fn new ( stdout : std:: process:: ChildStdout ) -> TsGoLintMessageStream {
650+ TsGoLintMessageStream { stdout, buffer : Vec :: with_capacity ( 8192 ) }
651+ }
652+ }
653+
654+ impl Iterator for TsGoLintMessageStream {
655+ type Item = Result < TsGoLintMessage , String > ;
656+
657+ fn next ( & mut self ) -> Option < Self :: Item > {
658+ let mut read_buf = [ 0u8 ; 8192 ] ;
659+
660+ loop {
661+ // Try to parse a complete message from the existing buffer
662+ let mut cursor = std:: io:: Cursor :: new ( self . buffer . as_slice ( ) ) ;
663+
664+ if cursor. position ( ) < self . buffer . len ( ) as u64 {
665+ let start_pos = cursor. position ( ) ;
666+ match parse_single_message ( & mut cursor) {
667+ Ok ( message) => {
668+ // Successfully parsed a message, remove it from buffer
669+ #[ expect( clippy:: cast_possible_truncation) ]
670+ self . buffer . drain ( ..cursor. position ( ) as usize ) ;
671+ return Some ( Ok ( message) ) ;
672+ }
673+ Err ( _) => {
674+ // Could not parse a complete message, need more data
675+ cursor. set_position ( start_pos) ;
676+ }
677+ }
678+ }
679+
680+ // Read more data from stdout
681+ match self . stdout . read ( & mut read_buf) {
682+ Ok ( 0 ) => {
683+ return None ;
684+ }
685+ Ok ( n) => {
686+ self . buffer . extend_from_slice ( & read_buf[ ..n] ) ;
687+ }
688+ Err ( e) => {
689+ return Some ( Err ( format ! ( "Failed to read from tsgolint stdout: {e}" ) ) ) ;
690+ }
691+ }
624692 }
625693 }
626694}
@@ -693,8 +761,8 @@ fn parse_single_message(cursor: &mut std::io::Cursor<&[u8]>) -> Result<TsGoLintM
693761 if cursor. read_exact ( & mut message_type_byte) . is_err ( ) {
694762 return Err ( "Failed to read message type byte" . to_string ( ) ) ;
695763 }
696- let message_type = MessageType :: from_u8 ( message_type_byte[ 0 ] )
697- . ok_or_else ( | | "Invalid message type byte" . to_string ( ) ) ?;
764+ let message_type = MessageType :: try_from ( message_type_byte[ 0 ] )
765+ . map_err ( |_ | "Invalid message type byte" . to_string ( ) ) ?;
698766
699767 let mut payload_bytes = vec ! [ 0u8 ; size] ;
700768 if cursor. read_exact ( & mut payload_bytes) . is_err ( ) {
0 commit comments