From 186d9557d65517a4d1f24c11c98347bbe3d2e580 Mon Sep 17 00:00:00 2001 From: hermes Date: Sun, 18 Oct 2026 12:15:40 +0000 Subject: [PATCH] fix(core): the IPOT table body holds plain row markup only One tbody start and end in the page, in any case, and inside it only tr, td and span tags with no '<' before a tag closes. A stray no longer cuts the table short, and cells inside a comment or an attribute are no longer read as brokers (Codex re-check). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh --- crates/idx-core/src/api/ipot/parse.rs | 40 +++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/idx-core/src/api/ipot/parse.rs b/crates/idx-core/src/api/ipot/parse.rs index 1b97ef1..516d4ae 100644 --- a/crates/idx-core/src/api/ipot/parse.rs +++ b/crates/idx-core/src/api/ipot/parse.rs @@ -131,6 +131,25 @@ fn broker_kind(class: Option<&str>) -> Option { } } +/// Whether `body` (lowercased) holds only `tr`, `td` and `span` tags, each +/// closed before the next `<`: no comments, other elements, or markup +/// inside attributes that the row checks could miscount. +fn plain_markup(body: &str) -> bool { + body.match_indices('<').all(|(at, _)| { + let tag = &body[at + 1..]; + let Some(end) = tag.find('>') else { + return false; + }; + let inner = &tag[..end]; + let name: String = inner + .trim_start_matches('/') + .chars() + .take_while(char::is_ascii_alphanumeric) + .collect(); + !inner.contains('<') && matches!(name.as_str(), "tr" | "td" | "span") + }) +} + /// The `MM/DD/YYYY` value of the input with this id. fn input_date(html: &str, id: &str) -> Option { let at = html.find(&format!("id=\"{id}\""))?; @@ -159,9 +178,12 @@ pub fn parse_broker_summary(html: &str) -> Result { .find("") .map(|len| (start + "".len(), start + len)) }); - let Some((body_start, body_end)) = body.filter(|_| lower.matches("", "") .replace("