moonbit-icd

MoonBit ICD-10/ICD-11 parsing, catalog queries, delimited import, batch validation, deterministic reports, and CLI workflows.

icd
healthcare
validation
catalog
cli
parser
moon add Ycq-68798/moonbit-icd@0.2.2
Download zip
Author
Version
0.2.2
License
Apache-2.0
Last updated
3 hours ago
Downloads
5
README

#moonbit-icd

moonbit-icd is a reusable MoonBit toolkit for structured ICD coding workflows. It covers code normalization and parsing, chapter/range resolution, catalog queries, authorized data import/export, batch validation, deterministic reports, and a small icd-check command-line interface.

The library is designed for health-information prototypes, claims-data preprocessing, research annotation, teaching tools, and other software that needs structural coding checks. It is not a diagnostic system and does not replace jurisdiction-specific coding guidance.

#Core capabilities

  • Strict ICD-10 category/subcategory parsing and ICD-11 stem/extension parsing.
  • Canonical normalization for user-entered codes and safe invalid-input errors.
  • ICD-10 chapter boundaries, code ranges, overlap checks, and partitioning.
  • Catalog construction with duplicate, chapter, parent, and exclusion checks.
  • Exact, prefix, title, chapter, hierarchy, exclusion, and ranked-search queries.
  • CSV/TSV import/export with quoted-field support and line/column diagnostics.
  • Batch validation with valid/unknown/invalid counts and chapter distributions.
  • Text, TSV, and JSON-like deterministic report rendering.
  • A representative, hand-authored fixture catalog used by tests and examples.

#Quick start

Install the MoonBit toolchain, then run the project from its module root:

moon check --target all --deny-warn moon test --target all --deny-warn moon run cmd/main -- check I10 moon run cmd/main -- chapter J45 moon run cmd/main -- prefix A00 moon run cmd/main -- search diabetes moon run cmd/main -- batch I10,B20,bad moon run cmd/main -- stats

The package namespace is Ycq-68798/moonbit-icd. A consumer can depend on the module through moon.mod and use the public root package APIs.

#Library example

///|
test {
let code = parse_icd10(" a00.0 ")
inspect(code.canonical(), content="A00.0")

let report = validate_icd10("B20")
debug_inspect(report.status, content="Valid")
assert_true(format_report(report).contains("exclusion hint"))
}

For an application-provided catalog, construct entries with IcdEntry::new, validate it with Catalog::from_entries, and then call validate_against or validate_batch. import_entries accepts the same six-column schema emitted by export_entries:

code,title,chapter_id,parent,excludes,note I10,Essential hypertension,IX,,,example

#CLI

The executable is intentionally thin and delegates to the library:

moon run cmd/main -- check I10 moon run cmd/main -- chapter J45 moon run cmd/main -- prefix A00 moon run cmd/main -- search diabetes moon run cmd/main -- batch I10,B20,bad moon run cmd/main -- stats

check distinguishes invalid syntax, syntactically valid codes absent from the current catalog, and catalog entries that are valid for the structural workflow. batch keeps input order so callers can correlate each result with an imported row.

#Architecture

AreaFilesResponsibility
Domain modeltypes.mbt, errors.mbtPublic records, statuses, and errors
Parsingnormalization.mbt, parser.mbtCanonical forms and ICD syntax
Rangesranges.mbt, range_tools.mbtChapters, rank boundaries, partitions
Catalogcatalog*.mbt, advanced_query.mbtInvariants, queries, search, fixtures
Data pipelinedelimited_reader.mbt, delimited_writer.mbtCSV/TSV import and export
Validationvalidate.mbt, batch_validation.mbt, validation_policy.mbtSingle and batch workflows
Reportinganalytics.mbt, report_rendering.mbt, data_quality.mbtMetrics, audits, deterministic output
Executablescmd/main, cmd/benchmarkCLI usage and reproducible workload

The root package has no external dependencies. This keeps consumers free to choose their own data source and serialization boundary.

#Data boundary and licensing

The repository contains representative hand-authored fixtures for software behavior, not a complete official ICD release. It does not redistribute a full WHO, ICD-10-CM, or national table. Production deployments should import data that they are authorized to use and should preserve that data's jurisdiction, version, attribution, and license metadata.

Repository source is licensed under Apache-2.0. See DATA-SOURCES.md for provenance and redistribution guidance.

#Benchmark

Run the deterministic benchmark workload locally:

moon run cmd/benchmark -- --rounds 1000

The command reports the actual rounds, workload size, status totals, and a checksum. It is paired with an external wall-clock measurement so results can be reproduced on a stated toolchain and machine; see BENCHMARKS.md.

#Testing and CI

Local checks matching CI are:

moon version --all moon update moon fmt --check moon check --target all --deny-warn moon test --target all --deny-warn moon test --target native --enable-coverage --deny-warn moon info git diff --exit-code

GitHub Actions runs the MoonBit stable installer on Ubuntu, macOS, and Windows, checks all supported backends, runs native coverage where available, and rejects formatting or generated-interface drift.

#Contributing

See CONTRIBUTING.md for the development loop, test expectations, and data-source requirements. Bug reports should include the smallest reproducible code, toolchain version, target backend, and command output. Never include private clinical records or other sensitive data.

#License

Apache-2.0. See LICENSE.

#
CatalogError

pub(all) suberror CatalogError {
DuplicateCode(String)
InvalidEntry(String, String)
MissingReference(String, String)
} derive(Eq, ToJson,
Debug
)

#
IcdParseError

pub(all) suberror IcdParseError {
EmptyInput
UnsupportedEdition(String)
InvalidCode(String)
} derive(Eq, ToJson,
Debug
)

#
BatchItem

pub struct BatchItem {
line : Int
input : String
report : ValidationReport
} derive(Eq, ToJson,
Debug
)

#
BatchReport

pub struct BatchReport {
items : Array[BatchItem]
summary : BatchSummary
} derive(Eq, ToJson,
Debug
)

#
BatchSummary

pub struct BatchSummary {
total : Int
valid : Int
unknown : Int
invalid : Int
chapters : Array[ChapterCount]
} derive(Eq, ToJson,
Debug
)

#
Catalog

pub struct Catalog {
entries : Array[IcdEntry]
} derive(
Debug
)

#
Catalog::all

fn Catalog::all(self : Catalog) -> Array[IcdEntry]

#
Catalog::ancestors

fn Catalog::ancestors(self : Catalog, code : String) -> Array[IcdEntry]

#
Catalog::children

fn Catalog::children(self : Catalog, parent : String) -> Array[IcdEntry]

#
Catalog::exclusions

fn Catalog::exclusions(self : Catalog, code : String) -> Array[IcdEntry]

#
Catalog::from_entries

fn Catalog::from_entries(entries : Array[IcdEntry]) -> Catalog raise CatalogError

#
Catalog::in_chapter

fn Catalog::in_chapter(self : Catalog, chapter_id : String) -> Array[IcdEntry]

#
Catalog::len

fn Catalog::len(self : Catalog) -> Int

#
Catalog::lookup

fn Catalog::lookup(self : Catalog, code : String) -> IcdEntry?

#
Catalog::prefix

fn Catalog::prefix(self : Catalog, prefix : String) -> Array[IcdEntry]

fn Catalog::ranked_search(self : Catalog, query : String, limit : Int) -> Array[SearchHit]

#
Catalog::search_title

fn Catalog::search_title(self : Catalog, query : String) -> Array[IcdEntry]

#
CatalogAudit

pub struct CatalogAudit {
entries : Int
chapter_coverage : Int
missing_parents : Int
missing_exclusions : Int
duplicate_codes : Int
invalid_codes : Int
} derive(Eq, ToJson,
Debug
)

#
CatalogAudit::is_healthy

fn CatalogAudit::is_healthy(self : CatalogAudit) -> Bool

#
ChapterCount

pub struct ChapterCount {
chapter_id : String
total : Int
valid : Int
unknown : Int
invalid : Int
} derive(Eq, ToJson,
Debug
)

#
CodeRange

pub struct CodeRange {
start : String
end : String
start_rank : Int
end_rank : Int
} derive(Eq, ToJson,
Debug
)

#
CodeRange::contains

fn CodeRange::contains(self : CodeRange, code : String) -> Bool

#
CodeRange::new

fn CodeRange::new(start : String, end : String) -> CodeRange raise IcdParseError

#
CodeRange::overlaps

fn CodeRange::overlaps(self : CodeRange, other : CodeRange) -> Bool

#
CodeRange::partition

fn CodeRange::partition(self : CodeRange, codes : Array[String]) -> RangePartition

#
Delimiter

pub enum Delimiter {
Comma
Tab
} derive(Eq, ToJson,
Debug
)

#
Delimiter::comma

fn Delimiter::comma() -> Delimiter

#
Delimiter::tab

fn Delimiter::tab() -> Delimiter

#
IcdChapter

pub struct IcdChapter {
id : String
title : String
start : String
end : String
note : String
} derive(Eq, ToJson,
Debug
)

#
IcdChapter::contains

fn IcdChapter::contains(self : IcdChapter, code : IcdCode) -> Bool

#
IcdCode

pub struct IcdCode {
edition : IcdEdition
raw : String
stem : String
category : String
subcategory : String?
numeric : Int
extension : String?
} derive(Eq, ToJson,
Debug
)

#
IcdCode::canonical

fn IcdCode::canonical(self : IcdCode) -> String

#
IcdEdition

pub enum IcdEdition {
ICD10
ICD11
} derive(Eq, ToJson,
Debug
)

#
IcdEntry

pub struct IcdEntry {
code : String
title : String
chapter_id : String
parent : String?
excludes : Array[String]
note : String
} derive(Eq, ToJson,
Debug
)

#
IcdEntry::new

fn IcdEntry::new(code : String, title : String, chapter_id : String, parent : String?, excludes : Array[String], note : String) -> IcdEntry

#
ImportDiagnostic

pub struct ImportDiagnostic {
line : Int
column : Int
message : String
hint : String
} derive(Eq, ToJson,
Debug
)

#
ImportOptions

pub struct ImportOptions {
has_header : Bool
delimiter : Delimiter
reject_unknown_columns : Bool
} derive(Eq, ToJson,
Debug
)

#
ImportOptions::csv

#
ImportOptions::new

fn ImportOptions::new(has_header : Bool, delimiter : Delimiter, reject_unknown_columns : Bool) -> ImportOptions

#
ImportOptions::tsv

#
ImportResult

pub struct ImportResult {
entries : Array[IcdEntry]
diagnostics : Array[ImportDiagnostic]
} derive(Eq, ToJson,
Debug
)

#
NormalizationResult

pub struct NormalizationResult {
accepted : Array[String]
rejected : Array[String]
} derive(Eq, ToJson,
Debug
)

#
RangePartition

pub struct RangePartition {
inside : Array[String]
outside : Array[String]
invalid : Array[String]
} derive(Eq, ToJson,
Debug
)

#
SearchHit

pub struct SearchHit {
code : String
title : String
score : Int
} derive(Eq, ToJson,
Debug
)

#
ValidationIssue

pub struct ValidationIssue {
code : String
message : String
hint : String
} derive(Eq, ToJson,
Debug
)

#
ValidationPolicy

pub struct ValidationPolicy {
require_catalog_membership : Bool
} derive(Eq, ToJson,
Debug
)

#
ValidationPolicy::permissive

#
ValidationPolicy::strict

#
ValidationReport

pub struct ValidationReport {
input : String
normalized : String?
status : ValidationStatus
chapter : IcdChapter?
entry : IcdEntry?
issues : Array[ValidationIssue]
} derive(Eq, ToJson,
Debug
)

#
ValidationStatus

pub enum ValidationStatus {
Valid
Unknown
Invalid
} derive(Eq, ToJson,
Debug
)

#
audit_catalog

fn audit_catalog(catalog : Catalog) -> CatalogAudit

#
audit_entries

fn audit_entries(entries : Array[IcdEntry]) -> CatalogAudit

#
bundled_catalog

fn bundled_catalog() -> Catalog

#
canonicalize_lines

fn canonicalize_lines(text : String) -> NormalizationResult

#
chapters_for_range

fn chapters_for_range(start : String, end : String) -> Array[IcdChapter]

#
children_of

fn children_of(parent : String) -> Array[IcdEntry]

#
exclusion_hints

fn exclusion_hints(entry : IcdEntry) -> Array[IcdEntry]

#
execute_cli

fn execute_cli(args : Array[String]) -> String

#
export_entries

fn export_entries(entries : Array[IcdEntry], delimiter : Delimiter) -> String

#
find_chapter

fn find_chapter(code : IcdCode) -> IcdChapter?

#
find_entry

fn find_entry(code : String) -> IcdEntry?

#
format_report

fn format_report(report : ValidationReport) -> String

#
icd10_chapters

fn icd10_chapters() -> Array[IcdChapter]

#
import_entries

fn import_entries(text : String, options : ImportOptions) -> ImportResult

#
is_valid_category

fn is_valid_category(code : String) -> Bool

#
normalize_code

fn normalize_code(input : String) -> String

#
normalize_codes

fn normalize_codes(inputs : Array[String]) -> NormalizationResult

#
parse_code

fn parse_code(input : String, edition? : IcdEdition) -> IcdCode raise IcdParseError

#
parse_icd10

fn parse_icd10(input : String) -> IcdCode raise IcdParseError

#
parse_icd11

fn parse_icd11(input : String) -> IcdCode raise IcdParseError

#
query_prefix

fn query_prefix(prefix : String) -> Array[IcdEntry]

#
render_batch_json

fn render_batch_json(report : BatchReport) -> String

#
render_batch_text

fn render_batch_text(report : BatchReport) -> String

#
render_batch_tsv

fn render_batch_tsv(report : BatchReport) -> String

#
sample_entries

fn sample_entries() -> Array[IcdEntry]

#
summarize

fn summarize(reports : Array[ValidationReport]) -> BatchSummary

#
validate_against

fn validate_against(catalog : Catalog, input : String) -> ValidationReport

#
validate_batch

fn validate_batch(catalog : Catalog, inputs : Array[String]) -> BatchReport

#
validate_icd10

fn validate_icd10(input : String) -> ValidationReport

#
validate_with_policy

fn validate_with_policy(catalog : Catalog, input : String, policy : ValidationPolicy) -> ValidationReport

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io