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