feat: core domain types and Filter predicate
This commit is contained in:
+143
-1
@@ -1 +1,143 @@
|
||||
// implemented in a later task
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AlphaIdEntry {
|
||||
pub alpha_id: String,
|
||||
pub valid: bool,
|
||||
pub icd_primary: String, // Field 3, normalized below
|
||||
pub icd_star: String, // Field 4
|
||||
pub icd_addon: String, // Field 5 ("!") — NOT ICD exclusion
|
||||
pub icd_primary2: String, // Field 6
|
||||
pub orpha: String, // Field 7
|
||||
pub text: String, // Field 8
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct IcdMeta {
|
||||
pub code: String, // normalized, no + * !
|
||||
pub description: String,
|
||||
pub chapter: String,
|
||||
pub group: String,
|
||||
pub para295: String, // P/O/Z/V
|
||||
pub para301: String,
|
||||
pub exotic: bool,
|
||||
pub ifsg: bool,
|
||||
pub content: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Tags {
|
||||
pub billable: bool, // para295 == "P"
|
||||
pub valid: bool, // Alpha-ID field 1
|
||||
pub chapter: String,
|
||||
pub exotic: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Filter {
|
||||
pub billable_only: bool,
|
||||
pub valid_only: bool,
|
||||
pub chapters: Option<Vec<String>>,
|
||||
pub exclude_exotic: bool,
|
||||
}
|
||||
|
||||
impl Default for Filter {
|
||||
fn default() -> Self {
|
||||
Filter { billable_only: false, valid_only: false, chapters: None, exclude_exotic: false }
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn accepts(&self, t: &Tags) -> bool {
|
||||
if self.billable_only && !t.billable { return false; }
|
||||
if self.valid_only && !t.valid { return false; }
|
||||
if self.exclude_exotic && t.exotic { return false; }
|
||||
if let Some(ch) = &self.chapters {
|
||||
if !ch.iter().any(|c| c == &t.chapter) { return false; }
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Mode { C, A }
|
||||
|
||||
impl FromStr for Mode {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
"c" => Ok(Mode::C),
|
||||
"a" => Ok(Mode::A),
|
||||
other => Err(format!("unknown mode: {other}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Candidate {
|
||||
pub icd_code: String,
|
||||
pub alpha_text: String,
|
||||
pub segment_idx: usize,
|
||||
pub lexical: Option<f32>,
|
||||
pub semantic: Option<f32>,
|
||||
pub rerank: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Suggestion {
|
||||
pub icd_code: String,
|
||||
pub description: String,
|
||||
pub score: f32,
|
||||
pub matched_phrase: String,
|
||||
pub source_segments: Vec<usize>,
|
||||
pub tags: Tags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub struct Diagnostics {
|
||||
pub mode: String,
|
||||
pub degraded: bool,
|
||||
pub segment_count: usize,
|
||||
pub ionos_calls: usize,
|
||||
pub millis: u128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct SuggestResult {
|
||||
pub suggestions: Vec<Suggestion>,
|
||||
pub diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Config {
|
||||
pub ionos_base_url: String,
|
||||
pub token_path: String,
|
||||
pub embed_model: String,
|
||||
pub rerank_model: String,
|
||||
pub alpha_id_path: String,
|
||||
pub claml_path: String,
|
||||
pub index_dir: String,
|
||||
pub pool_size: usize,
|
||||
pub top_k: usize,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: &str) -> Result<Config, AppError> {
|
||||
let s = std::fs::read_to_string(path)
|
||||
.map_err(|e| AppError::Io(format!("config {path}: {e}")))?;
|
||||
toml::from_str(&s).map_err(|e| AppError::Parse(format!("config: {e}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AppError {
|
||||
#[error("io error: {0}")]
|
||||
Io(String),
|
||||
#[error("parse error: {0}")]
|
||||
Parse(String),
|
||||
#[error("ionos error: {0}")]
|
||||
Ionos(String),
|
||||
#[error("config error: {0}")]
|
||||
Config(String),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use alpha_id::model::{Filter, Tags, Mode};
|
||||
|
||||
#[test]
|
||||
fn filter_billable_only_rejects_non_primary() {
|
||||
let tags = Tags { billable: false, valid: true, chapter: "I".into(), exotic: false };
|
||||
let f = Filter { billable_only: true, valid_only: false, chapters: None, exclude_exotic: false };
|
||||
assert!(!f.accepts(&tags));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_default_accepts_everything() {
|
||||
let tags = Tags { billable: false, valid: false, chapter: "II".into(), exotic: true };
|
||||
assert!(Filter::default().accepts(&tags));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mode_parses_from_str() {
|
||||
assert_eq!("a".parse::<Mode>().unwrap(), Mode::A);
|
||||
assert_eq!("c".parse::<Mode>().unwrap(), Mode::C);
|
||||
}
|
||||
Reference in New Issue
Block a user