///|
fn is_ascii_letter(c : Char) -> Bool {
('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
}
///|
fn is_digit_char(c : Char) -> Bool {
'0' <= c && c <= '9'
}
///|
fn compact_code(input : String) -> String {
normalize_code(input)
}
///|
fn digit_value(c : UInt16) -> Int {
c.to_int() - ('0' : UInt16).to_int()
}
///|
fn parse_two_digits(s : String, at~ : Int) -> Int? {
if at + 1 >= s.length() {
None
} else if is_digit_char(s.get_char(at).unwrap()) &&
is_digit_char(s.get_char(at + 1).unwrap()) {
Some(digit_value(s[at]) * 10 + digit_value(s[at + 1]))
} else {
None
}
}
///|
pub fn parse_icd10(input : String) -> IcdCode raise IcdParseError {
let cleaned = compact_code(input)
if cleaned.is_empty() {
raise IcdParseError::EmptyInput
}
if cleaned.length() < 3 {
raise IcdParseError::InvalidCode(input)
}
guard cleaned.get_char(0) is Some(letter) && is_ascii_letter(letter) else {
raise IcdParseError::InvalidCode(input)
}
guard parse_two_digits(cleaned, at=1) is Some(number) else {
raise IcdParseError::InvalidCode(input)
}
let category = cleaned[:3].to_owned()
let subcategory = if cleaned.length() > 3 {
if cleaned[3] != ('.' : UInt16) || cleaned.length() > 8 {
raise IcdParseError::InvalidCode(input)
}
let rest = cleaned[4:].to_owned()
if rest.is_empty() || !rest.all(is_digit_char) {
raise IcdParseError::InvalidCode(input)
}
Some(rest)
} else {
None
}
{
edition: ICD10,
raw: input,
stem: category,
category,
subcategory,
numeric: rank_parts(letter, number),
extension: None,
}
}
///|
pub fn parse_icd11(input : String) -> IcdCode raise IcdParseError {
let cleaned = compact_code(input)
if cleaned.is_empty() {
raise IcdParseError::EmptyInput
}
let stem = match cleaned.split_once("&") {
Some((left, right)) => {
if right.is_empty() {
raise IcdParseError::InvalidCode(input)
}
left.to_owned()
}
None => cleaned
}
if stem.length() != 4 ||
!(stem.get_char(0) is Some(first) &&
(is_digit_char(first) || is_ascii_letter(first))) ||
!stem[1:].all(fn(c) { is_digit_char(c) || is_ascii_letter(c) }) {
raise IcdParseError::InvalidCode(input)
}
{
edition: ICD11,
raw: input,
stem,
category: stem,
subcategory: None,
numeric: 0,
extension: match cleaned.after("&") {
Some(v) => Some(v.trim().to_owned().to_upper())
None => None
},
}
}
///|
pub fn parse_code(
input : String,
edition? : IcdEdition = ICD10,
) -> IcdCode raise IcdParseError {
match edition {
ICD10 => parse_icd10(input)
ICD11 => parse_icd11(input)
}
}