diff --git a/crates/idx-core/src/api/ipot/parse.rs b/crates/idx-core/src/api/ipot/parse.rs index c996ff5..74763e9 100644 --- a/crates/idx-core/src/api/ipot/parse.rs +++ b/crates/idx-core/src/api/ipot/parse.rs @@ -176,12 +176,14 @@ pub fn parse_broker_summary(html: &str) -> Result { // another shape, a rank out of order, or a broker after a side's blank // rows rejects the page. let (mut buyers_ended, mut sellers_ended) = (false, false); - for (index, row) in html[body_start..body_end].split("").enumerate() { - let row = cells(row); - if row.is_empty() { + let mut rank = 0; + for piece in html[body_start..body_end].split("") { + // Only the space after the last row has no row in it. + if !piece.contains("().ok() != Some(rank) { return Err(IdxError::ParseError(format!( "ipot broker summary: unexpected row {rank} ({} cells, rank {:?})", @@ -349,7 +351,19 @@ mod tests { 1, ); let gap = DAY.replacen(">XL<", "><", 1); - for page in [short_row, skipped_rank, gap, "".to_string()] { + // Rank 1 without its cell ends parses to no cells at all. + let body = DAY.find("").unwrap(); + let first_row_end = body + DAY[body..].find("").unwrap(); + let unclosed = DAY[..body].to_string() + + &DAY[body..first_row_end].replace("", "") + + &DAY[first_row_end..]; + for page in [ + short_row, + skipped_rank, + gap, + unclosed, + "".to_string(), + ] { assert!( matches!(parse_broker_summary(&page), Err(IdxError::ParseError(_))), "{}",