diff --git a/MANIFEST b/MANIFEST index 0f3c133..3849a55 100644 --- a/MANIFEST +++ b/MANIFEST @@ -38,6 +38,7 @@ lib/hl7-sanitize.sh lib/hl7-desanitize.sh lib/hl7-diff.sh lib/hl7-field.sh +lib/hl7-schema.sh # Generic helpers lib/each.sh diff --git a/VERSION b/VERSION index 1a5ac0d..faef31a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.9 +0.7.0 diff --git a/install-larry.sh b/install-larry.sh index 0e50279..9744628 100755 --- a/install-larry.sh +++ b/install-larry.sh @@ -111,6 +111,7 @@ fetch lib/nc-parse.sh "$LARRY_HOME/lib/nc-parse.sh" fetch lib/nc-inbound.sh "$LARRY_HOME/lib/nc-inbound.sh" fetch lib/nc-make-jump.sh "$LARRY_HOME/lib/nc-make-jump.sh" fetch lib/hl7-field.sh "$LARRY_HOME/lib/hl7-field.sh" +fetch lib/hl7-schema.sh "$LARRY_HOME/lib/hl7-schema.sh" fetch lib/nc-msgs.sh "$LARRY_HOME/lib/nc-msgs.sh" fetch lib/nc-document.sh "$LARRY_HOME/lib/nc-document.sh" fetch lib/nc-diff-interface.sh "$LARRY_HOME/lib/nc-diff-interface.sh" diff --git a/larry.sh b/larry.sh index 890a829..b82c3ce 100755 --- a/larry.sh +++ b/larry.sh @@ -47,7 +47,7 @@ set -o pipefail # ───────────────────────────────────────────────────────────────────────────── # Config # ───────────────────────────────────────────────────────────────────────────── -LARRY_VERSION="0.6.9" +LARRY_VERSION="0.7.0" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" LARRY_BASE_URL="${LARRY_BASE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main}" LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-${LARRY_BASE_URL}/larry.sh}" @@ -605,6 +605,15 @@ _resolve_lib_dir() { } LARRY_LIB_DIR="$(_resolve_lib_dir || echo '')" +# v0.7.0: HL7 v2.x schema for inline tab completion + /hl7 / /hl7-fields slash +# commands. Sourced (not executed) so the bash assoc arrays live in our shell. +# Silently no-ops on bash <4 (assoc arrays unavailable); the REPL still works, +# just without HL7 tab completion. +if [ -n "$LARRY_LIB_DIR" ] && [ -r "$LARRY_LIB_DIR/hl7-schema.sh" ]; then + # shellcheck disable=SC1090,SC1091 + . "$LARRY_LIB_DIR/hl7-schema.sh" 2>/dev/null || true +fi + _lib_err_if_missing() { [ -n "$LARRY_LIB_DIR" ] && return 0 echo "ERROR: lib/ tools not found. Looked in \$(dirname \$0)/lib and \$LARRY_HOME/lib." @@ -2397,6 +2406,22 @@ Slash commands: 6-phase regression across SSH-aliased envs (e.g. /nc-regression-env dev qa server) + HL7 schema lookup (v0.7.0): + /hl7 print the field list for an HL7 segment + (e.g. /hl7 PID → all 30 PID fields) + /hl7 (no arg) list all known HL7 segments + /hl7-fields print component breakdown for a field + (e.g. /hl7-fields PID.5 → Family, Given, ...) + + Mouse mode (v0.7.0): + /mouse on|off toggle xterm mouse + bracketed-paste for the + session. Status with /mouse (no arg). + Env: LARRY_NO_MOUSE=1 disables at startup. + Caveat: click-to-position-cursor in the + input line is terminal-dependent; iTerm2 + and modern macOS Terminal forward clicks; + MobaXterm/Cygwin behaviour varies. + PHI inline syntax in any prompt: @@VALUE EASY: wrap PHI in @@. Spaceless = no end delim. e.g. @@12345 @@SMITH^JOHN @@V789 @@ -2435,15 +2460,27 @@ Status line (v0.6.9): Disable entirely with LARRY_NO_STATUS=1. Force re-display with /status. Suppressed automatically on the first turn (no data yet). -TAB completion (v0.6.6/v0.6.7): +TAB completion (v0.6.6/v0.6.7/v0.7.0): Type '/' followed by any prefix and press TAB. /h → /help /ss → lists every /ssh-* command with one-line descriptions /ssh-h → /ssh-hosts /q → /quit Subsequence fuzzy is the fallback when no prefix matches (e.g. /sssp finds - /ssh-setup). After @, file-path completion kicks in instead. Non-slash - input falls back to a literal tab. + /ssh-setup). After @, file-path completion kicks in instead. + + HL7 inline completion (v0.7.0): tab-complete segments, fields, and + components while you type a prompt. + M → MSH (single match) + PI → PID (single match) + PID. → lists all 30 PID fields with descriptions + PID.3 → completes to "PID.3 " (trailing space) + PID.5. → lists PID.5 components (Family Name, Given Name, ...) + PID.5.1 → completes to "PID.5.1 " + Z-segments (site-specific) are not in the built-in schema; tab on Z + prints a one-line hint. + + Non-slash input not matching any of the above falls back to a literal tab. EOF } @@ -2524,6 +2561,9 @@ _LARRY_SLASH_CMDS=( /show-last-tool /nc-diff-env /nc-regression-env + /hl7 + /hl7-fields + /mouse ) # _LARRY_SLASH_CMDS_DESC — one-line descriptions for each slash command. @@ -2571,6 +2611,9 @@ _LARRY_SLASH_CMDS_DESC=( [/show-last-tool]="print full last tool call + result for debugging" [/nc-diff-env]=" [pattern] diff NetConfigs across two SSH-aliased envs" [/nc-regression-env]=" [scope] 6-phase regression across SSH-aliased envs" + [/hl7]=" print full field list for an HL7 segment (e.g. /hl7 PID)" + [/hl7-fields]=" print component breakdown (e.g. /hl7-fields PID.5)" + [/mouse]="on|off toggle xterm mouse mode for this session" ) # __larry_complete_slash — bound to TAB via `bind -x` (see _install_readline_tab). @@ -2631,6 +2674,43 @@ __larry_complete_slash() { return 0 fi + # v0.7.0: HL7-aware tab completion. + # Extract the trailing whitespace-delimited token of $pre and test for HL7 + # shapes. If none match, fall through to slash-command / literal-tab logic. + local hl7_token="" + case "$pre" in + ''|*[[:space:]]) hl7_token="" ;; + *) hl7_token="${pre##*[[:space:]]}" ;; + esac + # Recognised HL7 shapes: + # 1) ^[A-Z]{1,3}$ partial segment ID (e.g. M, MS, MSH, PI) + # 2) ^[A-Z]{3}\.\d*$ field within segment (e.g. PID., PID.3, MSH.10) + # 3) ^[A-Z]{3}\.\d+\.\d*$ component within field (e.g. PID.5., PID.5.1) + if [ -n "$hl7_token" ] && [ -n "${_HL7_SCHEMA_LOADED:-}" ]; then + case "$hl7_token" in + [A-Z]|[A-Z][A-Z]|[A-Z][A-Z][A-Z]) + __larry_complete_hl7_segment "$hl7_token" + return 0 + ;; + [A-Z][A-Z][A-Z].*) + # Split on dots to discriminate field vs. component. + local _hl7_rest="${hl7_token#???.}" # drop "SEG." + case "$_hl7_rest" in + *.*) + # Two dots in the token — component completion (SEG.N.M*). + __larry_complete_hl7_component "$hl7_token" + return 0 + ;; + ''|[0-9]*) + # Field completion (SEG.N*). Allow empty (just "PID.") and digit-only. + __larry_complete_hl7_field "$hl7_token" + return 0 + ;; + esac + ;; + esac + fi + # Only complete when the buffer is a single token starting with '/'. # If there's whitespace before the cursor, we treat it as "user typing # arguments to a command", not "user wants to complete the command name". @@ -2710,6 +2790,180 @@ __larry_complete_slash() { # Zero matches → silent no-op (the user's typo stays on screen so they can fix it). } +# __larry_complete_hl7_segment PARTIAL +# Complete an HL7 segment ID at the cursor. PARTIAL is 1..3 uppercase letters. +# - Exactly one match → replace with the full segment ID (no trailing space +# so the user can keep typing ".") +# - Multiple matches → list them with descriptions +# - Zero matches → if PARTIAL starts with Z, print a Z-segment hint; +# else silent no-op +__larry_complete_hl7_segment() { + local partial="$1" + local line="$READLINE_LINE" + local point="${READLINE_POINT:-0}" + local pre="${line:0:point}" + local post="${line:point}" + # Locate the start of the partial inside pre so we can splice the replacement. + local pre_head="${pre%"$partial"}" + + local matches=() s + while IFS= read -r s; do + [ -n "$s" ] && case "$s" in + "$partial"*) matches+=("$s") ;; + esac + done < <(hl7_segments) + + if [ "${#matches[@]}" -eq 1 ]; then + READLINE_LINE="${pre_head}${matches[0]}${post}" + READLINE_POINT=$((${#pre_head} + ${#matches[0]})) + return 0 + fi + + # If the partial is itself an exact segment ID AND has more prefix-matches, + # treat the exact match as the chosen completion (add a dot so the user can + # continue typing the field). Common case: "MSH" with MSA also in the schema. + if [ "${#matches[@]}" -gt 1 ] && [ -n "$(hl7_seg_desc "$partial")" ]; then + READLINE_LINE="${pre_head}${partial}.${post}" + READLINE_POINT=$((${#pre_head} + ${#partial} + 1)) + return 0 + fi + + if [ "${#matches[@]}" -gt 1 ]; then + printf '\n' + local m desc + for m in "${matches[@]}"; do + desc=$(hl7_seg_desc "$m") + printf ' %s%-6s%s %s%s%s\n' "$C_CYAN" "$m" "$C_RESET" "$C_DIM" "$desc" "$C_RESET" + done + return 0 + fi + + # No matches. Hint for Z-segments (site-specific, not baked in). + case "$partial" in + Z*) + printf '\n %s(Z-segments are site-specific; not in the built-in schema)%s\n' "$C_DIM" "$C_RESET" + ;; + esac + return 0 +} + +# __larry_complete_hl7_field TOKEN +# Complete an HL7 field within a segment. TOKEN looks like: +# PID. → list all 30 PID fields +# PID.1 → if unique completes to "PID.1 "; if many (1, 10..19) lists them +# PID.3 → unique, completes to "PID.3 " +__larry_complete_hl7_field() { + local token="$1" + local line="$READLINE_LINE" + local point="${READLINE_POINT:-0}" + local pre="${line:0:point}" + local post="${line:point}" + local pre_head="${pre%"$token"}" + + local seg="${token%%.*}" + local partial="${token#*.}" # may be empty + + # Unknown segment — nothing to do. + [ -z "$(hl7_seg_desc "$seg")" ] && return 0 + + # Gather candidate field indices that match the partial prefix. + local matches=() idx name line2 + while IFS=$'\t' read -r idx name; do + case "$idx" in + "$partial"*) matches+=("$idx"$'\t'"$name") ;; + esac + done < <(hl7_fields_for "$seg") + + if [ "${#matches[@]}" -eq 1 ]; then + # Single match: complete to "SEG.N " (trailing space). + local pair="${matches[0]}" + local i="${pair%%$'\t'*}" + local replacement="${seg}.${i} " + READLINE_LINE="${pre_head}${replacement}${post}" + READLINE_POINT=$((${#pre_head} + ${#replacement})) + return 0 + fi + + # If the partial is itself a valid exact field index AND there are other + # prefix-matches (e.g. PID.3 also prefix-matches PID.30), prefer the exact + # match — the user typed the complete number deliberately. + if [ "${#matches[@]}" -gt 1 ] && [ -n "$partial" ] && [ -n "$(hl7_field_name "${seg}.${partial}")" ]; then + local replacement="${seg}.${partial} " + READLINE_LINE="${pre_head}${replacement}${post}" + READLINE_POINT=$((${#pre_head} + ${#replacement})) + return 0 + fi + + if [ "${#matches[@]}" -gt 1 ]; then + printf '\n' + local pair i n key label + for pair in "${matches[@]}"; do + i="${pair%%$'\t'*}" + n="${pair#*$'\t'}" + label="${seg}.${i}" + printf ' %s%-12s%s %s%s%s\n' "$C_CYAN" "$label" "$C_RESET" "$C_DIM" "$n" "$C_RESET" + done + return 0 + fi + return 0 +} + +# __larry_complete_hl7_component TOKEN +# Complete an HL7 component within a field. TOKEN looks like: +# PID.5. → list all PID.5 components (Family, Given, ...) +# PID.5.1 → unique, completes to "PID.5.1 " +__larry_complete_hl7_component() { + local token="$1" + local line="$READLINE_LINE" + local point="${READLINE_POINT:-0}" + local pre="${line:0:point}" + local post="${line:point}" + local pre_head="${pre%"$token"}" + + # Split SEG.N.M-partial. We accept SEG = 3 uppercase letters. + local seg="${token%%.*}" + local rest="${token#*.}" # N.M-partial + local field="${rest%%.*}" + local partial="${rest#*.}" # may be empty + local key="${seg}.${field}" + + # Validate the field actually exists in the schema. + [ -z "$(hl7_field_name "$key")" ] && return 0 + + local matches=() idx name + while IFS=$'\t' read -r idx name; do + case "$idx" in + "$partial"*) matches+=("$idx"$'\t'"$name") ;; + esac + done < <(hl7_components_for "$key") + + if [ "${#matches[@]}" -eq 0 ]; then + # Field has no component breakdown defined. Print a one-line note so the + # user knows tab-complete didn't fail — the data just isn't there. + printf '\n %s(no component breakdown for %s in built-in schema)%s\n' "$C_DIM" "$key" "$C_RESET" + return 0 + fi + + if [ "${#matches[@]}" -eq 1 ]; then + local pair="${matches[0]}" + local m="${pair%%$'\t'*}" + local replacement="${key}.${m} " + READLINE_LINE="${pre_head}${replacement}${post}" + READLINE_POINT=$((${#pre_head} + ${#replacement})) + return 0 + fi + + printf '\n' + local pair m n label + for pair in "${matches[@]}"; do + m="${pair%%$'\t'*}" + n="${pair#*$'\t'}" + label="${key}.${m}" + printf ' %s%-14s%s %s%s%s\n' "$C_CYAN" "$label" "$C_RESET" "$C_DIM" "$n" "$C_RESET" + done + return 0 +} + # __larry_complete_atfile PARTIAL # Complete a file path for an @ reference. Uses fzf if on PATH for # an interactive picker; otherwise lists matches under the prompt and (if @@ -2788,6 +3042,67 @@ _install_readline_tab() { bind -x '"\t": __larry_complete_slash' 2>/dev/null || true } +# v0.7.0: mouse support in the REPL. +# +# What this *does* enable: +# - Bracketed-paste mode: terminal wraps pastes in \e[200~ ... \e[201~ so +# multi-line pastes don't accidentally trigger early Enter. Most modern +# terminals + readline (bind 'set enable-bracketed-paste on') do this +# already; we set it explicitly to be safe. +# - SGR mouse reporting (mode 1006): the terminal emits CSI ;x;yM / m +# for clicks. Cooperating terminals (iTerm2, modern macOS Terminal, +# xterm, kitty, alacritty) will forward these to the foreground process. +# +# What this *does not* attempt (yet): +# - Click-to-position cursor in the readline input line. Reliable across +# terminals would require: +# (a) parsing the CSI escape sequence in real time, +# (b) mapping (col,row) → buffer offset (which depends on the +# prompt-line wrap, terminal width, and any preceding output), +# (c) updating $READLINE_POINT from inside a `bind -x` handler bound +# to ESC. +# Bash readline lets you `bind -x '"\e[<": _handler'` but the handler +# fires *per byte* (no buffering of the rest of the sequence) on most +# bashes; the implementations that work require term-specific shims. +# We document the limitation and ship the safer subset. +# +# Kill switch: LARRY_NO_MOUSE=1 in the environment skips both enable and +# disable. /mouse on|off toggles at runtime. +# +# Refs: +# - xterm Control Sequences (Ctlseqs.txt) — modes 1000/1003/1006/2004. +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +# - readline 'set enable-bracketed-paste on' (~/.inputrc). +_LARRY_MOUSE_ACTIVE=0 +_install_mouse_mode() { + # Honour the env kill switch. + if [ "${LARRY_NO_MOUSE:-0}" = "1" ]; then + _LARRY_MOUSE_ACTIVE=0 + return 0 + fi + # Only attempt if we have a TTY. + [ -t 1 ] || return 0 + # Bracketed paste (terminal side). Idempotent in any decent terminal. + printf '\033[?2004h' 2>/dev/null || true + # Readline-side bracketed paste (so readline strips the wrapper bytes and + # treats the paste as one chunk rather than typed input). + bind 'set enable-bracketed-paste on' 2>/dev/null || true + # SGR-encoded mouse reporting (mode 1006). Use 1000 (X10 button events) as + # the base; 1003 (any-event including motion) is intentionally NOT enabled + # — it floods the input stream and can interfere with readline. + printf '\033[?1000h\033[?1006h' 2>/dev/null || true + _LARRY_MOUSE_ACTIVE=1 +} +_uninstall_mouse_mode() { + # Always emit the disable sequences even if we don't think it was on — + # cheap and prevents a borked terminal if our state tracking drifts. + [ -t 1 ] || return 0 + printf '\033[?1006l\033[?1000l' 2>/dev/null || true + _LARRY_MOUSE_ACTIVE=0 +} +# Ensure mouse mode is disabled on REPL exit (Ctrl-C, /quit, EOF). Idempotent. +trap '_uninstall_mouse_mode' EXIT INT TERM + read_user_input() { # Returns user input via global LARRY_INPUT. # If first line is "<<", read until line "EOF" (heredoc-style). @@ -2917,6 +3232,10 @@ main_loop() { stty erase '^?' 2>/dev/null || true fi + # v0.7.0: enable mouse mode (bracketed-paste + SGR mouse reporting). The + # trap installed in _install_mouse_mode tears this down on exit. + _install_mouse_mode + larry_say "${C_BOLD}Larry-Anywhere v$LARRY_VERSION${C_RESET} ready. Model: $LARRY_MODEL." larry_say "Type your message and press Enter. Use '<<' alone on a line to start multi-line (end with 'EOF'). /help for commands." echo "" @@ -2970,6 +3289,104 @@ main_loop() { fi fi continue ;; + # v0.7.0: HL7 schema lookup commands. + /hl7|/hl7\ *) + local _arg; _arg=$(_slash_args "/hl7" "$input") + if [ -z "${_HL7_SCHEMA_LOADED:-}" ]; then + err "HL7 schema not loaded (lib/hl7-schema.sh missing or bash <4)" + continue + fi + if [ -z "$_arg" ]; then + printf '%susage:%s /hl7 e.g. /hl7 PID\n' "$C_YELLOW" "$C_RESET" + printf '\n%sknown segments:%s\n' "$C_BOLD" "$C_RESET" + local _s _d + while IFS= read -r _s; do + _d=$(hl7_seg_desc "$_s") + printf ' %s%-6s%s %s%s%s\n' "$C_CYAN" "$_s" "$C_RESET" "$C_DIM" "$_d" "$C_RESET" + done < <(hl7_segments) + continue + fi + # Normalise to upper, drop a trailing dot if user typed "PID." + _arg=$(printf '%s' "$_arg" | tr '[:lower:]' '[:upper:]') + _arg="${_arg%.}" + if [ -z "$(hl7_seg_desc "$_arg")" ]; then + case "$_arg" in + Z*) err "$_arg looks like a site-specific Z-segment; not in the built-in schema" ;; + *) err "unknown segment: $_arg (try /hl7 with no args to list)" ;; + esac + continue + fi + printf '%s%s%s %s%s%s\n' "$C_BOLD$C_CYAN" "$_arg" "$C_RESET" "$C_DIM" "$(hl7_seg_desc "$_arg")" "$C_RESET" + local _i _n _label + while IFS=$'\t' read -r _i _n; do + _label="${_arg}.${_i}" + printf ' %s%-12s%s %s%s%s\n' "$C_CYAN" "$_label" "$C_RESET" "$C_DIM" "$_n" "$C_RESET" + done < <(hl7_fields_for "$_arg") + continue ;; + /hl7-fields|/hl7-fields\ *) + local _arg; _arg=$(_slash_args "/hl7-fields" "$input") + if [ -z "${_HL7_SCHEMA_LOADED:-}" ]; then + err "HL7 schema not loaded (lib/hl7-schema.sh missing or bash <4)" + continue + fi + if [ -z "$_arg" ]; then + err "usage: /hl7-fields e.g. /hl7-fields PID.5" + continue + fi + _arg=$(printf '%s' "$_arg" | tr '[:lower:]' '[:upper:]') + _arg="${_arg%.}" + case "$_arg" in + [A-Z][A-Z][A-Z].[0-9]*) : ;; + *) err "expected form SEG.N (3 uppercase letters, dot, number)"; continue ;; + esac + local _fname; _fname=$(hl7_field_name "$_arg") + if [ -z "$_fname" ]; then + err "unknown field: $_arg" + continue + fi + printf '%s%s%s %s%s%s\n' "$C_BOLD$C_CYAN" "$_arg" "$C_RESET" "$C_DIM" "$_fname" "$C_RESET" + local _has=0 _m _n _label + while IFS=$'\t' read -r _m _n; do + _has=1 + _label="${_arg}.${_m}" + printf ' %s%-14s%s %s%s%s\n' "$C_CYAN" "$_label" "$C_RESET" "$C_DIM" "$_n" "$C_RESET" + done < <(hl7_components_for "$_arg") + if [ "$_has" -eq 0 ]; then + printf ' %s(no component breakdown for %s in built-in schema)%s\n' "$C_DIM" "$_arg" "$C_RESET" + fi + continue ;; + # v0.7.0: mouse mode toggle (xterm SGR mouse + bracketed paste). + /mouse|/mouse\ *) + local _arg; _arg=$(_slash_args "/mouse" "$input") + case "${_arg:-status}" in + on) + LARRY_NO_MOUSE=0 + _install_mouse_mode + if [ "$_LARRY_MOUSE_ACTIVE" = "1" ]; then + larry_say "mouse mode ON (bracketed-paste + SGR mouse reporting; click-to-position is terminal-dependent)" + else + warn "mouse mode requested but no TTY detected" + fi + ;; + off) + _uninstall_mouse_mode + LARRY_NO_MOUSE=1 + larry_say "mouse mode OFF" + ;; + status) + if [ "${LARRY_NO_MOUSE:-0}" = "1" ]; then + larry_say "mouse mode: disabled (LARRY_NO_MOUSE=1). /mouse on to enable." + elif [ "$_LARRY_MOUSE_ACTIVE" = "1" ]; then + larry_say "mouse mode: active (bracketed-paste + SGR reporting)" + else + larry_say "mouse mode: inactive" + fi + ;; + *) + err "usage: /mouse on|off (no arg → status)" + ;; + esac + continue ;; /show-last-tool) if [ -z "$_LARRY_LAST_TOOL_NAME" ]; then err "no tool calls yet this session" diff --git a/lib/hl7-schema.sh b/lib/hl7-schema.sh new file mode 100755 index 0000000..89e74d5 --- /dev/null +++ b/lib/hl7-schema.sh @@ -0,0 +1,791 @@ +#!/usr/bin/env bash +# lib/hl7-schema.sh — HL7 v2.x segment + field + component schema, sourced by +# larry.sh for inline tab completion (v0.7.0) and the /hl7 / /hl7-fields +# slash commands. +# +# This file is *sourced*, not executed. It populates a small set of bash +# associative arrays and helper functions. No side effects beyond declaration. +# +# Scope of v0.7.0: +# - Core 8 segments fully fielded: MSH, PID, PV1, EVN, MSA, NK1, OBR, OBX +# - Additional 10 segments stubbed: PV2, ERR, GT1, IN1, IN2, ORC, AL1, DG1, +# PR1, ROL (top-1..N fields; deeper fields can be added in v0.7.x). +# - Component breakdowns: PID.3, PID.5, PID.11, MSH.9, OBX.5, NK1.4, IN1.4. +# +# Z-segments: per spec convention, anything starting with "Z" is site-specific. +# Larry will surface a hint instead of guessing when a Z* prefix is tab-completed. +# +# Sources cited inline by segment block (HL7 2.5 / 2.7 chapter references). +# When in doubt the canonical reference is HL7 v2.5.1 — see +# https://www.hl7.org/implement/standards/product_brief.cfm?product_id=144 +# +# Storage convention (bash 4 assoc arrays don't nest): +# _HL7_FIELD["SEG.N"]="Field Name" +# _HL7_COMP["SEG.N.M"]="Component Name" +# _HL7_SEG_LIST=("MSH" "PID" ...) # ordered enumeration +# _HL7_SEG_DESC["SEG"]="Human-readable name" +# _HL7_SEG_FIELD_MAX["SEG"]=N # highest field index defined +# +# A helper `hl7_segments` echoes the seg list; `hl7_fields_for SEG` echoes +# "N\tName" lines; `hl7_components_for SEG.N` echoes "M\tName" lines. + +# Idempotent: don't re-declare if already sourced this shell. +if [ -n "${_HL7_SCHEMA_LOADED:-}" ]; then + return 0 2>/dev/null || true +fi +_HL7_SCHEMA_LOADED=1 + +# Bash 4+ guard (assoc arrays). On older bash the file silently no-ops, which +# means tab-completion of HL7 just won't kick in — the rest of larry still works. +if ! declare -A _HL7_FIELD 2>/dev/null; then + return 0 2>/dev/null || true +fi +declare -A _HL7_COMP 2>/dev/null +declare -A _HL7_SEG_DESC 2>/dev/null +declare -A _HL7_SEG_FIELD_MAX 2>/dev/null +declare -a _HL7_SEG_LIST 2>/dev/null + +_hl7_reg_seg() { + # $1=SEG $2=description $3=max-field-index + _HL7_SEG_DESC["$1"]="$2" + _HL7_SEG_FIELD_MAX["$1"]="$3" + _HL7_SEG_LIST+=("$1") +} + +# ───────────────────────────────────────────────────────────────────────────── +# MSH — Message Header (HL7 2.5 §2.14.9) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "MSH" "Message Header" 21 +_HL7_FIELD["MSH.1"]="Field Separator" +_HL7_FIELD["MSH.2"]="Encoding Characters" +_HL7_FIELD["MSH.3"]="Sending Application" +_HL7_FIELD["MSH.4"]="Sending Facility" +_HL7_FIELD["MSH.5"]="Receiving Application" +_HL7_FIELD["MSH.6"]="Receiving Facility" +_HL7_FIELD["MSH.7"]="Date/Time Of Message" +_HL7_FIELD["MSH.8"]="Security" +_HL7_FIELD["MSH.9"]="Message Type" +_HL7_FIELD["MSH.10"]="Message Control ID" +_HL7_FIELD["MSH.11"]="Processing ID" +_HL7_FIELD["MSH.12"]="Version ID" +_HL7_FIELD["MSH.13"]="Sequence Number" +_HL7_FIELD["MSH.14"]="Continuation Pointer" +_HL7_FIELD["MSH.15"]="Accept Acknowledgment Type" +_HL7_FIELD["MSH.16"]="Application Acknowledgment Type" +_HL7_FIELD["MSH.17"]="Country Code" +_HL7_FIELD["MSH.18"]="Character Set" +_HL7_FIELD["MSH.19"]="Principal Language Of Message" +_HL7_FIELD["MSH.20"]="Alternate Character Set Handling Scheme" +_HL7_FIELD["MSH.21"]="Message Profile Identifier" + +# MSH.9 components — Message Type (MSG datatype: code^event^structure) +_HL7_COMP["MSH.9.1"]="Message Code (e.g. ADT)" +_HL7_COMP["MSH.9.2"]="Trigger Event (e.g. A08)" +_HL7_COMP["MSH.9.3"]="Message Structure (e.g. ADT_A01)" + +# ───────────────────────────────────────────────────────────────────────────── +# PID — Patient Identification (HL7 2.5 §3.4.2) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "PID" "Patient Identification" 30 +_HL7_FIELD["PID.1"]="Set ID - PID" +_HL7_FIELD["PID.2"]="Patient ID (External, deprecated)" +_HL7_FIELD["PID.3"]="Patient Identifier List (MRN)" +_HL7_FIELD["PID.4"]="Alternate Patient ID - PID (deprecated)" +_HL7_FIELD["PID.5"]="Patient Name" +_HL7_FIELD["PID.6"]="Mother's Maiden Name" +_HL7_FIELD["PID.7"]="Date/Time of Birth" +_HL7_FIELD["PID.8"]="Administrative Sex" +_HL7_FIELD["PID.9"]="Patient Alias (deprecated)" +_HL7_FIELD["PID.10"]="Race" +_HL7_FIELD["PID.11"]="Patient Address" +_HL7_FIELD["PID.12"]="County Code (deprecated)" +_HL7_FIELD["PID.13"]="Phone Number - Home" +_HL7_FIELD["PID.14"]="Phone Number - Business" +_HL7_FIELD["PID.15"]="Primary Language" +_HL7_FIELD["PID.16"]="Marital Status" +_HL7_FIELD["PID.17"]="Religion" +_HL7_FIELD["PID.18"]="Patient Account Number" +_HL7_FIELD["PID.19"]="SSN Number - Patient (deprecated)" +_HL7_FIELD["PID.20"]="Driver's License Number - Patient (deprecated)" +_HL7_FIELD["PID.21"]="Mother's Identifier" +_HL7_FIELD["PID.22"]="Ethnic Group" +_HL7_FIELD["PID.23"]="Birth Place" +_HL7_FIELD["PID.24"]="Multiple Birth Indicator" +_HL7_FIELD["PID.25"]="Birth Order" +_HL7_FIELD["PID.26"]="Citizenship" +_HL7_FIELD["PID.27"]="Veterans Military Status" +_HL7_FIELD["PID.28"]="Nationality (deprecated)" +_HL7_FIELD["PID.29"]="Patient Death Date and Time" +_HL7_FIELD["PID.30"]="Patient Death Indicator" + +# PID.3 components — Patient Identifier List (CX datatype) +_HL7_COMP["PID.3.1"]="ID Number" +_HL7_COMP["PID.3.2"]="Check Digit" +_HL7_COMP["PID.3.3"]="Check Digit Scheme" +_HL7_COMP["PID.3.4"]="Assigning Authority" +_HL7_COMP["PID.3.5"]="Identifier Type Code (MR, SS, etc.)" +_HL7_COMP["PID.3.6"]="Assigning Facility" +_HL7_COMP["PID.3.7"]="Effective Date" +_HL7_COMP["PID.3.8"]="Expiration Date" +_HL7_COMP["PID.3.9"]="Assigning Jurisdiction" +_HL7_COMP["PID.3.10"]="Assigning Agency or Department" + +# PID.5 components — Patient Name (XPN datatype) +_HL7_COMP["PID.5.1"]="Family Name" +_HL7_COMP["PID.5.2"]="Given Name" +_HL7_COMP["PID.5.3"]="Second and Further Given Names or Initials Thereof" +_HL7_COMP["PID.5.4"]="Suffix (e.g. JR or III)" +_HL7_COMP["PID.5.5"]="Prefix (e.g. DR)" +_HL7_COMP["PID.5.6"]="Degree (e.g. MD) (deprecated)" +_HL7_COMP["PID.5.7"]="Name Type Code" +_HL7_COMP["PID.5.8"]="Name Representation Code" + +# PID.11 components — Patient Address (XAD datatype) +_HL7_COMP["PID.11.1"]="Street Address" +_HL7_COMP["PID.11.2"]="Other Designation" +_HL7_COMP["PID.11.3"]="City" +_HL7_COMP["PID.11.4"]="State or Province" +_HL7_COMP["PID.11.5"]="Zip or Postal Code" +_HL7_COMP["PID.11.6"]="Country" +_HL7_COMP["PID.11.7"]="Address Type" +_HL7_COMP["PID.11.8"]="Other Geographic Designation" +_HL7_COMP["PID.11.9"]="County/Parish Code" + +# ───────────────────────────────────────────────────────────────────────────── +# PV1 — Patient Visit (HL7 2.5 §3.4.3) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "PV1" "Patient Visit" 52 +_HL7_FIELD["PV1.1"]="Set ID - PV1" +_HL7_FIELD["PV1.2"]="Patient Class" +_HL7_FIELD["PV1.3"]="Assigned Patient Location" +_HL7_FIELD["PV1.4"]="Admission Type" +_HL7_FIELD["PV1.5"]="Preadmit Number" +_HL7_FIELD["PV1.6"]="Prior Patient Location" +_HL7_FIELD["PV1.7"]="Attending Doctor" +_HL7_FIELD["PV1.8"]="Referring Doctor" +_HL7_FIELD["PV1.9"]="Consulting Doctor" +_HL7_FIELD["PV1.10"]="Hospital Service" +_HL7_FIELD["PV1.11"]="Temporary Location" +_HL7_FIELD["PV1.12"]="Preadmit Test Indicator" +_HL7_FIELD["PV1.13"]="Re-admission Indicator" +_HL7_FIELD["PV1.14"]="Admit Source" +_HL7_FIELD["PV1.15"]="Ambulatory Status" +_HL7_FIELD["PV1.16"]="VIP Indicator" +_HL7_FIELD["PV1.17"]="Admitting Doctor" +_HL7_FIELD["PV1.18"]="Patient Type" +_HL7_FIELD["PV1.19"]="Visit Number" +_HL7_FIELD["PV1.20"]="Financial Class" +_HL7_FIELD["PV1.21"]="Charge Price Indicator" +_HL7_FIELD["PV1.22"]="Courtesy Code" +_HL7_FIELD["PV1.23"]="Credit Rating" +_HL7_FIELD["PV1.24"]="Contract Code" +_HL7_FIELD["PV1.25"]="Contract Effective Date" +_HL7_FIELD["PV1.26"]="Contract Amount" +_HL7_FIELD["PV1.27"]="Contract Period" +_HL7_FIELD["PV1.28"]="Interest Code" +_HL7_FIELD["PV1.29"]="Transfer to Bad Debt Code" +_HL7_FIELD["PV1.30"]="Transfer to Bad Debt Date" +_HL7_FIELD["PV1.31"]="Bad Debt Agency Code" +_HL7_FIELD["PV1.32"]="Bad Debt Transfer Amount" +_HL7_FIELD["PV1.33"]="Bad Debt Recovery Amount" +_HL7_FIELD["PV1.34"]="Delete Account Indicator" +_HL7_FIELD["PV1.35"]="Delete Account Date" +_HL7_FIELD["PV1.36"]="Discharge Disposition" +_HL7_FIELD["PV1.37"]="Discharged to Location" +_HL7_FIELD["PV1.38"]="Diet Type" +_HL7_FIELD["PV1.39"]="Servicing Facility" +_HL7_FIELD["PV1.40"]="Bed Status (deprecated)" +_HL7_FIELD["PV1.41"]="Account Status" +_HL7_FIELD["PV1.42"]="Pending Location" +_HL7_FIELD["PV1.43"]="Prior Temporary Location" +_HL7_FIELD["PV1.44"]="Admit Date/Time" +_HL7_FIELD["PV1.45"]="Discharge Date/Time" +_HL7_FIELD["PV1.46"]="Current Patient Balance" +_HL7_FIELD["PV1.47"]="Total Charges" +_HL7_FIELD["PV1.48"]="Total Adjustments" +_HL7_FIELD["PV1.49"]="Total Payments" +_HL7_FIELD["PV1.50"]="Alternate Visit ID" +_HL7_FIELD["PV1.51"]="Visit Indicator" +_HL7_FIELD["PV1.52"]="Other Healthcare Provider" + +# PV1.3 components — Assigned Patient Location (PL datatype) +_HL7_COMP["PV1.3.1"]="Point of Care" +_HL7_COMP["PV1.3.2"]="Room" +_HL7_COMP["PV1.3.3"]="Bed" +_HL7_COMP["PV1.3.4"]="Facility" +_HL7_COMP["PV1.3.5"]="Location Status" +_HL7_COMP["PV1.3.6"]="Person Location Type" +_HL7_COMP["PV1.3.7"]="Building" +_HL7_COMP["PV1.3.8"]="Floor" +_HL7_COMP["PV1.3.9"]="Location Description" + +# ───────────────────────────────────────────────────────────────────────────── +# EVN — Event Type (HL7 2.5 §3.4.1) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "EVN" "Event Type" 7 +_HL7_FIELD["EVN.1"]="Event Type Code (deprecated)" +_HL7_FIELD["EVN.2"]="Recorded Date/Time" +_HL7_FIELD["EVN.3"]="Date/Time Planned Event" +_HL7_FIELD["EVN.4"]="Event Reason Code" +_HL7_FIELD["EVN.5"]="Operator ID" +_HL7_FIELD["EVN.6"]="Event Occurred" +_HL7_FIELD["EVN.7"]="Event Facility" + +# ───────────────────────────────────────────────────────────────────────────── +# MSA — Message Acknowledgment (HL7 2.5 §2.14.10) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "MSA" "Message Acknowledgment" 9 +_HL7_FIELD["MSA.1"]="Acknowledgment Code (AA/AE/AR)" +_HL7_FIELD["MSA.2"]="Message Control ID" +_HL7_FIELD["MSA.3"]="Text Message (deprecated, use ERR)" +_HL7_FIELD["MSA.4"]="Expected Sequence Number" +_HL7_FIELD["MSA.5"]="Delayed Acknowledgment Type (deprecated)" +_HL7_FIELD["MSA.6"]="Error Condition (deprecated, use ERR)" +_HL7_FIELD["MSA.7"]="Message Waiting Number" +_HL7_FIELD["MSA.8"]="Message Waiting Priority" +_HL7_FIELD["MSA.9"]="Original Acknowledgment Type" + +# ───────────────────────────────────────────────────────────────────────────── +# NK1 — Next of Kin / Associated Parties (HL7 2.5 §3.4.5) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "NK1" "Next of Kin / Associated Parties" 39 +_HL7_FIELD["NK1.1"]="Set ID - NK1" +_HL7_FIELD["NK1.2"]="Name" +_HL7_FIELD["NK1.3"]="Relationship" +_HL7_FIELD["NK1.4"]="Address" +_HL7_FIELD["NK1.5"]="Phone Number" +_HL7_FIELD["NK1.6"]="Business Phone Number" +_HL7_FIELD["NK1.7"]="Contact Role" +_HL7_FIELD["NK1.8"]="Start Date" +_HL7_FIELD["NK1.9"]="End Date" +_HL7_FIELD["NK1.10"]="Next of Kin / Associated Parties Job Title" +_HL7_FIELD["NK1.11"]="Next of Kin / Associated Parties Job Code/Class" +_HL7_FIELD["NK1.12"]="Next of Kin / Associated Parties Employee Number" +_HL7_FIELD["NK1.13"]="Organization Name - NK1" +_HL7_FIELD["NK1.14"]="Marital Status" +_HL7_FIELD["NK1.15"]="Administrative Sex" +_HL7_FIELD["NK1.16"]="Date/Time of Birth" +_HL7_FIELD["NK1.17"]="Living Dependency" +_HL7_FIELD["NK1.18"]="Ambulatory Status" +_HL7_FIELD["NK1.19"]="Citizenship" +_HL7_FIELD["NK1.20"]="Primary Language" +_HL7_FIELD["NK1.21"]="Living Arrangement" +_HL7_FIELD["NK1.22"]="Publicity Code" +_HL7_FIELD["NK1.23"]="Protection Indicator" +_HL7_FIELD["NK1.24"]="Student Indicator" +_HL7_FIELD["NK1.25"]="Religion" +_HL7_FIELD["NK1.26"]="Mother's Maiden Name" +_HL7_FIELD["NK1.27"]="Nationality" +_HL7_FIELD["NK1.28"]="Ethnic Group" +_HL7_FIELD["NK1.29"]="Contact Reason" +_HL7_FIELD["NK1.30"]="Contact Person's Name" +_HL7_FIELD["NK1.31"]="Contact Person's Telephone Number" +_HL7_FIELD["NK1.32"]="Contact Person's Address" +_HL7_FIELD["NK1.33"]="Next of Kin / Associated Party's Identifiers" +_HL7_FIELD["NK1.34"]="Job Status" +_HL7_FIELD["NK1.35"]="Race" +_HL7_FIELD["NK1.36"]="Handicap" +_HL7_FIELD["NK1.37"]="Contact Person Social Security Number" +_HL7_FIELD["NK1.38"]="Next of Kin Birth Place" +_HL7_FIELD["NK1.39"]="VIP Indicator" + +# NK1.4 components — Address (XAD datatype) +_HL7_COMP["NK1.4.1"]="Street Address" +_HL7_COMP["NK1.4.2"]="Other Designation" +_HL7_COMP["NK1.4.3"]="City" +_HL7_COMP["NK1.4.4"]="State or Province" +_HL7_COMP["NK1.4.5"]="Zip or Postal Code" +_HL7_COMP["NK1.4.6"]="Country" +_HL7_COMP["NK1.4.7"]="Address Type" + +# ───────────────────────────────────────────────────────────────────────────── +# OBR — Observation Request (HL7 2.5 §4.5.3) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "OBR" "Observation Request" 49 +_HL7_FIELD["OBR.1"]="Set ID - OBR" +_HL7_FIELD["OBR.2"]="Placer Order Number" +_HL7_FIELD["OBR.3"]="Filler Order Number" +_HL7_FIELD["OBR.4"]="Universal Service Identifier" +_HL7_FIELD["OBR.5"]="Priority (deprecated)" +_HL7_FIELD["OBR.6"]="Requested Date/Time (deprecated)" +_HL7_FIELD["OBR.7"]="Observation Date/Time" +_HL7_FIELD["OBR.8"]="Observation End Date/Time" +_HL7_FIELD["OBR.9"]="Collection Volume" +_HL7_FIELD["OBR.10"]="Collector Identifier" +_HL7_FIELD["OBR.11"]="Specimen Action Code" +_HL7_FIELD["OBR.12"]="Danger Code" +_HL7_FIELD["OBR.13"]="Relevant Clinical Information" +_HL7_FIELD["OBR.14"]="Specimen Received Date/Time" +_HL7_FIELD["OBR.15"]="Specimen Source" +_HL7_FIELD["OBR.16"]="Ordering Provider" +_HL7_FIELD["OBR.17"]="Order Callback Phone Number" +_HL7_FIELD["OBR.18"]="Placer Field 1" +_HL7_FIELD["OBR.19"]="Placer Field 2" +_HL7_FIELD["OBR.20"]="Filler Field 1" +_HL7_FIELD["OBR.21"]="Filler Field 2" +_HL7_FIELD["OBR.22"]="Results Rpt/Status Chng - Date/Time" +_HL7_FIELD["OBR.23"]="Charge to Practice" +_HL7_FIELD["OBR.24"]="Diagnostic Serv Sect ID" +_HL7_FIELD["OBR.25"]="Result Status" +_HL7_FIELD["OBR.26"]="Parent Result" +_HL7_FIELD["OBR.27"]="Quantity/Timing (deprecated)" +_HL7_FIELD["OBR.28"]="Result Copies To" +_HL7_FIELD["OBR.29"]="Parent" +_HL7_FIELD["OBR.30"]="Transportation Mode" +_HL7_FIELD["OBR.31"]="Reason for Study" +_HL7_FIELD["OBR.32"]="Principal Result Interpreter" +_HL7_FIELD["OBR.33"]="Assistant Result Interpreter" +_HL7_FIELD["OBR.34"]="Technician" +_HL7_FIELD["OBR.35"]="Transcriptionist" +_HL7_FIELD["OBR.36"]="Scheduled Date/Time" +_HL7_FIELD["OBR.37"]="Number of Sample Containers" +_HL7_FIELD["OBR.38"]="Transport Logistics of Collected Sample" +_HL7_FIELD["OBR.39"]="Collector's Comment" +_HL7_FIELD["OBR.40"]="Transport Arrangement Responsibility" +_HL7_FIELD["OBR.41"]="Transport Arranged" +_HL7_FIELD["OBR.42"]="Escort Required" +_HL7_FIELD["OBR.43"]="Planned Patient Transport Comment" +_HL7_FIELD["OBR.44"]="Procedure Code" +_HL7_FIELD["OBR.45"]="Procedure Code Modifier" +_HL7_FIELD["OBR.46"]="Placer Supplemental Service Information" +_HL7_FIELD["OBR.47"]="Filler Supplemental Service Information" +_HL7_FIELD["OBR.48"]="Medically Necessary Duplicate Procedure Reason" +_HL7_FIELD["OBR.49"]="Result Handling" + +# ───────────────────────────────────────────────────────────────────────────── +# OBX — Observation/Result (HL7 2.5 §7.4.2) +# ───────────────────────────────────────────────────────────────────────────── +_hl7_reg_seg "OBX" "Observation/Result" 18 +_HL7_FIELD["OBX.1"]="Set ID - OBX" +_HL7_FIELD["OBX.2"]="Value Type" +_HL7_FIELD["OBX.3"]="Observation Identifier" +_HL7_FIELD["OBX.4"]="Observation Sub-ID" +_HL7_FIELD["OBX.5"]="Observation Value" +_HL7_FIELD["OBX.6"]="Units" +_HL7_FIELD["OBX.7"]="References Range" +_HL7_FIELD["OBX.8"]="Abnormal Flags" +_HL7_FIELD["OBX.9"]="Probability" +_HL7_FIELD["OBX.10"]="Nature of Abnormal Test" +_HL7_FIELD["OBX.11"]="Observation Result Status" +_HL7_FIELD["OBX.12"]="Effective Date of Reference Range" +_HL7_FIELD["OBX.13"]="User Defined Access Checks" +_HL7_FIELD["OBX.14"]="Date/Time of the Observation" +_HL7_FIELD["OBX.15"]="Producer's ID" +_HL7_FIELD["OBX.16"]="Responsible Observer" +_HL7_FIELD["OBX.17"]="Observation Method" +_HL7_FIELD["OBX.18"]="Equipment Instance Identifier" + +# OBX.3 components — Observation Identifier (CE datatype) +_HL7_COMP["OBX.3.1"]="Identifier (e.g. LOINC code)" +_HL7_COMP["OBX.3.2"]="Text (human-readable name)" +_HL7_COMP["OBX.3.3"]="Name of Coding System (e.g. LN)" +_HL7_COMP["OBX.3.4"]="Alternate Identifier" +_HL7_COMP["OBX.3.5"]="Alternate Text" +_HL7_COMP["OBX.3.6"]="Name of Alternate Coding System" + +# ───────────────────────────────────────────────────────────────────────────── +# Stubbed additional segments (top-level fields, no component breakdown yet). +# Bryan: extend in v0.7.x as needed. +# ───────────────────────────────────────────────────────────────────────────── + +# PV2 — Patient Visit Additional Info (HL7 2.5 §3.4.4) +_hl7_reg_seg "PV2" "Patient Visit Additional" 49 +_HL7_FIELD["PV2.1"]="Prior Pending Location" +_HL7_FIELD["PV2.2"]="Accommodation Code" +_HL7_FIELD["PV2.3"]="Admit Reason" +_HL7_FIELD["PV2.4"]="Transfer Reason" +_HL7_FIELD["PV2.5"]="Patient Valuables" +_HL7_FIELD["PV2.6"]="Patient Valuables Location" +_HL7_FIELD["PV2.7"]="Visit User Code" +_HL7_FIELD["PV2.8"]="Expected Admit Date/Time" +_HL7_FIELD["PV2.9"]="Expected Discharge Date/Time" +_HL7_FIELD["PV2.10"]="Estimated Length of Inpatient Stay" +_HL7_FIELD["PV2.11"]="Actual Length of Inpatient Stay" +_HL7_FIELD["PV2.12"]="Visit Description" +_HL7_FIELD["PV2.13"]="Referral Source Code" +_HL7_FIELD["PV2.14"]="Previous Service Date" +_HL7_FIELD["PV2.15"]="Employment Illness Related Indicator" +_HL7_FIELD["PV2.16"]="Purge Status Code" +_HL7_FIELD["PV2.17"]="Purge Status Date" +_HL7_FIELD["PV2.18"]="Special Program Code" +_HL7_FIELD["PV2.19"]="Retention Indicator" +_HL7_FIELD["PV2.20"]="Expected Number of Insurance Plans" +_HL7_FIELD["PV2.21"]="Visit Publicity Code" +_HL7_FIELD["PV2.22"]="Visit Protection Indicator" +_HL7_FIELD["PV2.23"]="Clinic Organization Name" +_HL7_FIELD["PV2.24"]="Patient Status Code" +_HL7_FIELD["PV2.25"]="Visit Priority Code" +_HL7_FIELD["PV2.26"]="Previous Treatment Date" +_HL7_FIELD["PV2.27"]="Expected Discharge Disposition" +_HL7_FIELD["PV2.28"]="Signature on File Date" +_HL7_FIELD["PV2.29"]="First Similar Illness Date" +_HL7_FIELD["PV2.30"]="Patient Charge Adjustment Code" +_HL7_FIELD["PV2.31"]="Recurring Service Code" +_HL7_FIELD["PV2.32"]="Billing Media Code" +_HL7_FIELD["PV2.33"]="Expected Surgery Date and Time" +_HL7_FIELD["PV2.34"]="Military Partnership Code" +_HL7_FIELD["PV2.35"]="Military Non-Availability Code" +_HL7_FIELD["PV2.36"]="Newborn Baby Indicator" +_HL7_FIELD["PV2.37"]="Baby Detained Indicator" +_HL7_FIELD["PV2.38"]="Mode of Arrival Code" +_HL7_FIELD["PV2.39"]="Recreational Drug Use Code" +_HL7_FIELD["PV2.40"]="Admission Level of Care Code" +_HL7_FIELD["PV2.41"]="Precaution Code" +_HL7_FIELD["PV2.42"]="Patient Condition Code" +_HL7_FIELD["PV2.43"]="Living Will Code" +_HL7_FIELD["PV2.44"]="Organ Donor Code" +_HL7_FIELD["PV2.45"]="Advance Directive Code" +_HL7_FIELD["PV2.46"]="Patient Status Effective Date" +_HL7_FIELD["PV2.47"]="Expected LOA Return Date/Time" +_HL7_FIELD["PV2.48"]="Expected Pre-admission Testing Date/Time" +_HL7_FIELD["PV2.49"]="Notify Clergy Code" + +# ERR — Error (HL7 2.5 §2.14.5) +_hl7_reg_seg "ERR" "Error" 12 +_HL7_FIELD["ERR.1"]="Error Code and Location (deprecated)" +_HL7_FIELD["ERR.2"]="Error Location" +_HL7_FIELD["ERR.3"]="HL7 Error Code" +_HL7_FIELD["ERR.4"]="Severity" +_HL7_FIELD["ERR.5"]="Application Error Code" +_HL7_FIELD["ERR.6"]="Application Error Parameter" +_HL7_FIELD["ERR.7"]="Diagnostic Information" +_HL7_FIELD["ERR.8"]="User Message" +_HL7_FIELD["ERR.9"]="Inform Person Indicator" +_HL7_FIELD["ERR.10"]="Override Type" +_HL7_FIELD["ERR.11"]="Override Reason Code" +_HL7_FIELD["ERR.12"]="Help Desk Contact Point" + +# GT1 — Guarantor (HL7 2.5 §6.5.5) +_hl7_reg_seg "GT1" "Guarantor" 50 +_HL7_FIELD["GT1.1"]="Set ID - GT1" +_HL7_FIELD["GT1.2"]="Guarantor Number" +_HL7_FIELD["GT1.3"]="Guarantor Name" +_HL7_FIELD["GT1.4"]="Guarantor Spouse Name" +_HL7_FIELD["GT1.5"]="Guarantor Address" +_HL7_FIELD["GT1.6"]="Guarantor Ph Num - Home" +_HL7_FIELD["GT1.7"]="Guarantor Ph Num - Business" +_HL7_FIELD["GT1.8"]="Guarantor Date/Time of Birth" +_HL7_FIELD["GT1.9"]="Guarantor Administrative Sex" +_HL7_FIELD["GT1.10"]="Guarantor Type" +_HL7_FIELD["GT1.11"]="Guarantor Relationship" +_HL7_FIELD["GT1.12"]="Guarantor SSN" +_HL7_FIELD["GT1.13"]="Guarantor Date - Begin" +_HL7_FIELD["GT1.14"]="Guarantor Date - End" +_HL7_FIELD["GT1.15"]="Guarantor Priority" +_HL7_FIELD["GT1.16"]="Guarantor Employer Name" +_HL7_FIELD["GT1.17"]="Guarantor Employer Address" +_HL7_FIELD["GT1.18"]="Guarantor Employer Phone Number" +_HL7_FIELD["GT1.19"]="Guarantor Employee ID Number" +_HL7_FIELD["GT1.20"]="Guarantor Employment Status" +_HL7_FIELD["GT1.21"]="Guarantor Organization Name" +_HL7_FIELD["GT1.22"]="Guarantor Billing Hold Flag" +_HL7_FIELD["GT1.23"]="Guarantor Credit Rating Code" +_HL7_FIELD["GT1.24"]="Guarantor Death Date And Time" +_HL7_FIELD["GT1.25"]="Guarantor Death Flag" +_HL7_FIELD["GT1.26"]="Guarantor Charge Adjustment Code" +_HL7_FIELD["GT1.27"]="Guarantor Household Annual Income" +_HL7_FIELD["GT1.28"]="Guarantor Household Size" +_HL7_FIELD["GT1.29"]="Guarantor Employer ID Number" +_HL7_FIELD["GT1.30"]="Guarantor Marital Status Code" +_HL7_FIELD["GT1.31"]="Guarantor Hire Effective Date" +_HL7_FIELD["GT1.32"]="Employment Stop Date" +_HL7_FIELD["GT1.33"]="Living Dependency" +_HL7_FIELD["GT1.34"]="Ambulatory Status" +_HL7_FIELD["GT1.35"]="Citizenship" +_HL7_FIELD["GT1.36"]="Primary Language" +_HL7_FIELD["GT1.37"]="Living Arrangement" +_HL7_FIELD["GT1.38"]="Publicity Code" +_HL7_FIELD["GT1.39"]="Protection Indicator" +_HL7_FIELD["GT1.40"]="Student Indicator" +_HL7_FIELD["GT1.41"]="Religion" +_HL7_FIELD["GT1.42"]="Mother's Maiden Name" +_HL7_FIELD["GT1.43"]="Nationality" +_HL7_FIELD["GT1.44"]="Ethnic Group" +_HL7_FIELD["GT1.45"]="Contact Person's Name" +_HL7_FIELD["GT1.46"]="Contact Person's Telephone Number" +_HL7_FIELD["GT1.47"]="Contact Reason" +_HL7_FIELD["GT1.48"]="Contact Relationship Code" +_HL7_FIELD["GT1.49"]="Job Title" +_HL7_FIELD["GT1.50"]="Job Code/Class" + +# IN1 — Insurance (HL7 2.5 §6.5.6) +_hl7_reg_seg "IN1" "Insurance" 53 +_HL7_FIELD["IN1.1"]="Set ID - IN1" +_HL7_FIELD["IN1.2"]="Insurance Plan ID" +_HL7_FIELD["IN1.3"]="Insurance Company ID" +_HL7_FIELD["IN1.4"]="Insurance Company Name" +_HL7_FIELD["IN1.5"]="Insurance Company Address" +_HL7_FIELD["IN1.6"]="Insurance Co Contact Person" +_HL7_FIELD["IN1.7"]="Insurance Co Phone Number" +_HL7_FIELD["IN1.8"]="Group Number" +_HL7_FIELD["IN1.9"]="Group Name" +_HL7_FIELD["IN1.10"]="Insured's Group Emp ID" +_HL7_FIELD["IN1.11"]="Insured's Group Emp Name" +_HL7_FIELD["IN1.12"]="Plan Effective Date" +_HL7_FIELD["IN1.13"]="Plan Expiration Date" +_HL7_FIELD["IN1.14"]="Authorization Information" +_HL7_FIELD["IN1.15"]="Plan Type" +_HL7_FIELD["IN1.16"]="Name Of Insured" +_HL7_FIELD["IN1.17"]="Insured's Relationship To Patient" +_HL7_FIELD["IN1.18"]="Insured's Date Of Birth" +_HL7_FIELD["IN1.19"]="Insured's Address" +_HL7_FIELD["IN1.20"]="Assignment Of Benefits" +_HL7_FIELD["IN1.21"]="Coordination Of Benefits" +_HL7_FIELD["IN1.22"]="Coord Of Ben. Priority" +_HL7_FIELD["IN1.23"]="Notice Of Admission Flag" +_HL7_FIELD["IN1.24"]="Notice Of Admission Date" +_HL7_FIELD["IN1.25"]="Report Of Eligibility Flag" +_HL7_FIELD["IN1.26"]="Report Of Eligibility Date" +_HL7_FIELD["IN1.27"]="Release Information Code" +_HL7_FIELD["IN1.28"]="Pre-Admit Cert (PAC)" +_HL7_FIELD["IN1.29"]="Verification Date/Time" +_HL7_FIELD["IN1.30"]="Verification By" +_HL7_FIELD["IN1.31"]="Type Of Agreement Code" +_HL7_FIELD["IN1.32"]="Billing Status" +_HL7_FIELD["IN1.33"]="Lifetime Reserve Days" +_HL7_FIELD["IN1.34"]="Delay Before L.R. Day" +_HL7_FIELD["IN1.35"]="Company Plan Code" +_HL7_FIELD["IN1.36"]="Policy Number" +_HL7_FIELD["IN1.37"]="Policy Deductible" +_HL7_FIELD["IN1.38"]="Policy Limit - Amount (deprecated)" +_HL7_FIELD["IN1.39"]="Policy Limit - Days" +_HL7_FIELD["IN1.40"]="Room Rate - Semi-Private (deprecated)" +_HL7_FIELD["IN1.41"]="Room Rate - Private (deprecated)" +_HL7_FIELD["IN1.42"]="Insured's Employment Status" +_HL7_FIELD["IN1.43"]="Insured's Administrative Sex" +_HL7_FIELD["IN1.44"]="Insured's Employer's Address" +_HL7_FIELD["IN1.45"]="Verification Status" +_HL7_FIELD["IN1.46"]="Prior Insurance Plan ID" +_HL7_FIELD["IN1.47"]="Coverage Type" +_HL7_FIELD["IN1.48"]="Handicap" +_HL7_FIELD["IN1.49"]="Insured's ID Number" +_HL7_FIELD["IN1.50"]="Signature Code" +_HL7_FIELD["IN1.51"]="Signature Code Date" +_HL7_FIELD["IN1.52"]="Insured's Birth Place" +_HL7_FIELD["IN1.53"]="VIP Indicator" + +# IN1.4 components — Insurance Company Name (XON datatype) +_HL7_COMP["IN1.4.1"]="Organization Name" +_HL7_COMP["IN1.4.2"]="Organization Name Type Code" +_HL7_COMP["IN1.4.3"]="ID Number (deprecated)" +_HL7_COMP["IN1.4.4"]="Check Digit" +_HL7_COMP["IN1.4.5"]="Check Digit Scheme" +_HL7_COMP["IN1.4.6"]="Assigning Authority" +_HL7_COMP["IN1.4.7"]="Identifier Type Code" +_HL7_COMP["IN1.4.8"]="Assigning Facility" +_HL7_COMP["IN1.4.9"]="Name Representation Code" +_HL7_COMP["IN1.4.10"]="Organization Identifier" + +# IN2 — Insurance Additional Info (truncated to 30; full HL7 list is 70) +_hl7_reg_seg "IN2" "Insurance Additional" 30 +_HL7_FIELD["IN2.1"]="Insured's Employee ID" +_HL7_FIELD["IN2.2"]="Insured's Social Security Number" +_HL7_FIELD["IN2.3"]="Insured's Employer's Name and ID" +_HL7_FIELD["IN2.4"]="Employer Information Data" +_HL7_FIELD["IN2.5"]="Mail Claim Party" +_HL7_FIELD["IN2.6"]="Medicare Health Ins Card Number" +_HL7_FIELD["IN2.7"]="Medicaid Case Name" +_HL7_FIELD["IN2.8"]="Medicaid Case Number" +_HL7_FIELD["IN2.9"]="Military Sponsor Name (deprecated)" +_HL7_FIELD["IN2.10"]="Military ID Number" +_HL7_FIELD["IN2.11"]="Dependent Of Military Recipient" +_HL7_FIELD["IN2.12"]="Military Organization" +_HL7_FIELD["IN2.13"]="Military Station" +_HL7_FIELD["IN2.14"]="Military Service" +_HL7_FIELD["IN2.15"]="Military Rank/Grade" +_HL7_FIELD["IN2.16"]="Military Status" +_HL7_FIELD["IN2.17"]="Military Retire Date" +_HL7_FIELD["IN2.18"]="Military Non-Avail Cert On File" +_HL7_FIELD["IN2.19"]="Baby Coverage" +_HL7_FIELD["IN2.20"]="Combine Baby Bill" +_HL7_FIELD["IN2.21"]="Blood Deductible (deprecated)" +_HL7_FIELD["IN2.22"]="Special Coverage Approval Name" +_HL7_FIELD["IN2.23"]="Special Coverage Approval Title" +_HL7_FIELD["IN2.24"]="Non-Covered Insurance Code" +_HL7_FIELD["IN2.25"]="Payor ID" +_HL7_FIELD["IN2.26"]="Payor Subscriber ID" +_HL7_FIELD["IN2.27"]="Eligibility Source" +_HL7_FIELD["IN2.28"]="Room Coverage Type/Amount" +_HL7_FIELD["IN2.29"]="Policy Type/Amount" +_HL7_FIELD["IN2.30"]="Daily Deductible" + +# ORC — Common Order (HL7 2.5 §4.5.1) +_hl7_reg_seg "ORC" "Common Order" 32 +_HL7_FIELD["ORC.1"]="Order Control" +_HL7_FIELD["ORC.2"]="Placer Order Number" +_HL7_FIELD["ORC.3"]="Filler Order Number" +_HL7_FIELD["ORC.4"]="Placer Group Number" +_HL7_FIELD["ORC.5"]="Order Status" +_HL7_FIELD["ORC.6"]="Response Flag" +_HL7_FIELD["ORC.7"]="Quantity/Timing (deprecated)" +_HL7_FIELD["ORC.8"]="Parent Order" +_HL7_FIELD["ORC.9"]="Date/Time of Transaction" +_HL7_FIELD["ORC.10"]="Entered By" +_HL7_FIELD["ORC.11"]="Verified By" +_HL7_FIELD["ORC.12"]="Ordering Provider" +_HL7_FIELD["ORC.13"]="Enterer's Location" +_HL7_FIELD["ORC.14"]="Call Back Phone Number" +_HL7_FIELD["ORC.15"]="Order Effective Date/Time" +_HL7_FIELD["ORC.16"]="Order Control Code Reason" +_HL7_FIELD["ORC.17"]="Entering Organization" +_HL7_FIELD["ORC.18"]="Entering Device" +_HL7_FIELD["ORC.19"]="Action By" +_HL7_FIELD["ORC.20"]="Advanced Beneficiary Notice Code" +_HL7_FIELD["ORC.21"]="Ordering Facility Name" +_HL7_FIELD["ORC.22"]="Ordering Facility Address" +_HL7_FIELD["ORC.23"]="Ordering Facility Phone Number" +_HL7_FIELD["ORC.24"]="Ordering Provider Address" +_HL7_FIELD["ORC.25"]="Order Status Modifier" +_HL7_FIELD["ORC.26"]="Advanced Beneficiary Notice Override Reason" +_HL7_FIELD["ORC.27"]="Filler's Expected Availability Date/Time" +_HL7_FIELD["ORC.28"]="Confidentiality Code" +_HL7_FIELD["ORC.29"]="Order Type" +_HL7_FIELD["ORC.30"]="Enterer Authorization Mode" +_HL7_FIELD["ORC.31"]="Parent Universal Service Identifier" +_HL7_FIELD["ORC.32"]="Advanced Beneficiary Notice Date" + +# AL1 — Patient Allergy Information (HL7 2.5 §3.4.6) +_hl7_reg_seg "AL1" "Patient Allergy" 6 +_HL7_FIELD["AL1.1"]="Set ID - AL1" +_HL7_FIELD["AL1.2"]="Allergen Type Code" +_HL7_FIELD["AL1.3"]="Allergen Code/Mnemonic/Description" +_HL7_FIELD["AL1.4"]="Allergy Severity Code" +_HL7_FIELD["AL1.5"]="Allergy Reaction Code" +_HL7_FIELD["AL1.6"]="Identification Date (deprecated)" + +# DG1 — Diagnosis (HL7 2.5 §6.5.2) +_hl7_reg_seg "DG1" "Diagnosis" 22 +_HL7_FIELD["DG1.1"]="Set ID - DG1" +_HL7_FIELD["DG1.2"]="Diagnosis Coding Method (deprecated)" +_HL7_FIELD["DG1.3"]="Diagnosis Code - DG1" +_HL7_FIELD["DG1.4"]="Diagnosis Description (deprecated)" +_HL7_FIELD["DG1.5"]="Diagnosis Date/Time" +_HL7_FIELD["DG1.6"]="Diagnosis Type" +_HL7_FIELD["DG1.7"]="Major Diagnostic Category (deprecated)" +_HL7_FIELD["DG1.8"]="Diagnostic Related Group (deprecated)" +_HL7_FIELD["DG1.9"]="DRG Approval Indicator (deprecated)" +_HL7_FIELD["DG1.10"]="DRG Grouper Review Code (deprecated)" +_HL7_FIELD["DG1.11"]="Outlier Type (deprecated)" +_HL7_FIELD["DG1.12"]="Outlier Days (deprecated)" +_HL7_FIELD["DG1.13"]="Outlier Cost (deprecated)" +_HL7_FIELD["DG1.14"]="Grouper Version And Type (deprecated)" +_HL7_FIELD["DG1.15"]="Diagnosis Priority" +_HL7_FIELD["DG1.16"]="Diagnosing Clinician" +_HL7_FIELD["DG1.17"]="Diagnosis Classification" +_HL7_FIELD["DG1.18"]="Confidential Indicator" +_HL7_FIELD["DG1.19"]="Attestation Date/Time" +_HL7_FIELD["DG1.20"]="Diagnosis Identifier" +_HL7_FIELD["DG1.21"]="Diagnosis Action Code" +_HL7_FIELD["DG1.22"]="Parent Diagnosis" + +# PR1 — Procedures (HL7 2.5 §6.5.4) +_hl7_reg_seg "PR1" "Procedures" 25 +_HL7_FIELD["PR1.1"]="Set ID - PR1" +_HL7_FIELD["PR1.2"]="Procedure Coding Method (deprecated)" +_HL7_FIELD["PR1.3"]="Procedure Code" +_HL7_FIELD["PR1.4"]="Procedure Description (deprecated)" +_HL7_FIELD["PR1.5"]="Procedure Date/Time" +_HL7_FIELD["PR1.6"]="Procedure Functional Type" +_HL7_FIELD["PR1.7"]="Procedure Minutes" +_HL7_FIELD["PR1.8"]="Anesthesiologist (deprecated)" +_HL7_FIELD["PR1.9"]="Anesthesia Code" +_HL7_FIELD["PR1.10"]="Anesthesia Minutes" +_HL7_FIELD["PR1.11"]="Surgeon (deprecated)" +_HL7_FIELD["PR1.12"]="Procedure Practitioner (deprecated)" +_HL7_FIELD["PR1.13"]="Consent Code" +_HL7_FIELD["PR1.14"]="Procedure Priority" +_HL7_FIELD["PR1.15"]="Associated Diagnosis Code" +_HL7_FIELD["PR1.16"]="Procedure Code Modifier" +_HL7_FIELD["PR1.17"]="Procedure DRG Type" +_HL7_FIELD["PR1.18"]="Tissue Type Code" +_HL7_FIELD["PR1.19"]="Procedure Identifier" +_HL7_FIELD["PR1.20"]="Procedure Action Code" +_HL7_FIELD["PR1.21"]="DRG Procedure Determination Status" +_HL7_FIELD["PR1.22"]="DRG Procedure Relevance" +_HL7_FIELD["PR1.23"]="Treating Clinician" +_HL7_FIELD["PR1.24"]="Treatment Type" +_HL7_FIELD["PR1.25"]="Associated Diagnosis" + +# ROL — Role (HL7 2.5 §15.4.2) +_hl7_reg_seg "ROL" "Role" 13 +_HL7_FIELD["ROL.1"]="Role Instance ID" +_HL7_FIELD["ROL.2"]="Action Code" +_HL7_FIELD["ROL.3"]="Role-ROL" +_HL7_FIELD["ROL.4"]="Role Person" +_HL7_FIELD["ROL.5"]="Role Begin Date/Time" +_HL7_FIELD["ROL.6"]="Role End Date/Time" +_HL7_FIELD["ROL.7"]="Role Duration" +_HL7_FIELD["ROL.8"]="Role Action Reason" +_HL7_FIELD["ROL.9"]="Provider Type" +_HL7_FIELD["ROL.10"]="Organization Unit Type" +_HL7_FIELD["ROL.11"]="Office/Home Address/Birthplace" +_HL7_FIELD["ROL.12"]="Phone" +_HL7_FIELD["ROL.13"]="Person's Location" + +# ───────────────────────────────────────────────────────────────────────────── +# Public helpers (read-only) +# ───────────────────────────────────────────────────────────────────────────── + +# hl7_segments — echo each registered segment ID on its own line. +hl7_segments() { + local s + for s in "${_HL7_SEG_LIST[@]}"; do + printf '%s\n' "$s" + done +} + +# hl7_seg_desc SEG — echo the description for SEG (empty if unknown). +hl7_seg_desc() { + local s="${1:-}" + [ -z "$s" ] && return 0 + printf '%s' "${_HL7_SEG_DESC[$s]:-}" +} + +# hl7_seg_max SEG — echo the max field index for SEG (empty if unknown). +hl7_seg_max() { + local s="${1:-}" + [ -z "$s" ] && return 0 + printf '%s' "${_HL7_SEG_FIELD_MAX[$s]:-}" +} + +# hl7_fields_for SEG — echo "N\tName" for every defined field of SEG, in order. +hl7_fields_for() { + local s="${1:-}" max i name + [ -z "$s" ] && return 0 + max="${_HL7_SEG_FIELD_MAX[$s]:-0}" + for ((i=1; i<=max; i++)); do + name="${_HL7_FIELD[$s.$i]:-}" + [ -n "$name" ] && printf '%d\t%s\n' "$i" "$name" + done +} + +# hl7_components_for SEG.N — echo "M\tName" for every defined component of SEG.N. +hl7_components_for() { + local key="${1:-}" m name + [ -z "$key" ] && return 0 + # Iterate possible components 1..30 (most datatypes top out under 20). + for m in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30; do + name="${_HL7_COMP[$key.$m]:-}" + [ -n "$name" ] && printf '%d\t%s\n' "$m" "$name" + done +} + +# hl7_field_name SEG.N — echo the field name (empty if undefined). +hl7_field_name() { + printf '%s' "${_HL7_FIELD[${1:-}]:-}" +} + +# hl7_component_name SEG.N.M — echo the component name (empty if undefined). +hl7_component_name() { + printf '%s' "${_HL7_COMP[${1:-}]:-}" +} + +# Sources cited: +# - HL7 v2.5.1 standard, chapters 2 (control), 3 (ADT), 4 (orders), 6 +# (financial), 7 (observation), 15 (personnel mgmt). +# https://www.hl7.org/implement/standards/product_brief.cfm?product_id=144 +# - Caristix HL7-Definition reference (mirror of segment tables): +# https://hl7-definition.caristix.com/v2/HL7v2.5.1 +# - Cloverleaf user community confirms these segment field names are stable +# across 2.3 → 2.7 for the fields listed above (deprecated flags noted +# inline where applicable).