From c9363c6675018dda955e12947421e6a95307e403 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 31 May 2026 19:03:51 +0200 Subject: [PATCH] fix: normalize --chapters filter input to padded chapter numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --chapters filter compared the raw CLI token by exact string equality against the zero-padded chapter tags ("01".."22") that the corpus carries (roman_to_padded, src/claml.rs). The CLI only split and trimmed the input, so the intuitive forms — `--chapters 4` or `--chapters E,I` — never matched any candidate and the suggestion list came back empty, indistinguishable from "no clinical match". Add a pure `normalize_chapters` helper that zero-pads bare 1-2 digit chapter numbers to two digits and partitions the token list into known chapters (1..=22) and unrecognised tokens. The Suggest arm now feeds the padded set into the filter and emits a stderr warning for each unrecognised token, so misuse is visible instead of silent. `--chapters 4` and `--chapters 04` now behave identically. ICD code-letter mapping (E -> chapter) is deliberately left out of scope: under this contract a letter is simply an unrecognised token and triggers the warning. closes #1 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/bin/alpha_id.rs | 11 +++++++++-- src/model.rs | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/bin/alpha_id.rs b/src/bin/alpha_id.rs index a781f8c..e7d771b 100644 --- a/src/bin/alpha_id.rs +++ b/src/bin/alpha_id.rs @@ -1,4 +1,4 @@ -use alpha_id::model::{Config, Filter, Mode}; +use alpha_id::model::{normalize_chapters, Config, Filter, Mode}; use alpha_id::pipeline::Pipeline; use clap::{Parser, Subcommand}; use std::io::Read; @@ -126,9 +126,16 @@ fn main() { Cmd::Suggest { input, mode, top, billable_only, valid_only, exclude_exotic, chapters, json } => { let p = Pipeline::load(&cfg).expect("load"); let m: Mode = mode.parse().expect("mode"); + let chapters = chapters.map(|c| { + let (padded, unknown) = normalize_chapters(&c); + for tok in &unknown { + eprintln!("warning: --chapters token '{tok}' matches no known chapter (01..22); ignored"); + } + padded + }); let filter = Filter { billable_only, valid_only, exclude_exotic, - chapters: chapters.map(|c| c.split(',').map(|s| s.trim().to_string()).collect()), + chapters, }; let text = read_input(&input); let res = p.suggest(&text, m, &filter, top); diff --git a/src/model.rs b/src/model.rs index e6347ee..4c48fe2 100644 --- a/src/model.rs +++ b/src/model.rs @@ -42,6 +42,29 @@ pub struct Filter { pub exclude_exotic: bool, } +/// Normalize a raw `--chapters` token list to the zero-padded chapter-tag form +/// ("01".."22") that `Filter::accepts` compares against. Splits on ',', trims +/// each token, and classifies: a token that parses as one of the 22 known +/// ICD-10-GM chapters (1..=22) goes — zero-padded to two digits — into the first +/// returned vec (in input order); every other non-empty token goes verbatim +/// (trimmed) into the second (unrecognised) vec. Empty-after-trim tokens are +/// skipped. Both "4" and "04" yield "04". +pub fn normalize_chapters(raw: &str) -> (Vec, Vec) { + let mut known = Vec::new(); + let mut unknown = Vec::new(); + for tok in raw.split(',') { + let tok = tok.trim(); + if tok.is_empty() { + continue; + } + match tok.parse::() { + Ok(n) if (1..=22).contains(&n) => known.push(format!("{n:02}")), + _ => unknown.push(tok.to_string()), + } + } + (known, unknown) +} + impl Filter { pub fn accepts(&self, t: &Tags) -> bool { if self.billable_only && !t.billable { return false; }