Skip to content

Commit 490b93a

Browse files
committed
refactor(tsgolint): make MessageType parsing more idomatic (#14299)
1 parent a24c36e commit 490b93a

File tree

1 file changed

+75
-7
lines changed

1 file changed

+75
-7
lines changed

crates/oxc_linter/src/tsgolint.rs

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -610,17 +610,85 @@ pub struct Suggestion {
610610
}
611611

612612
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
613+
#[repr(u8)]
613614
pub 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

Comments
 (0)