use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use hl7v2::{ValidationReport, ValidationReportIssue};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
pub status: HealthStatus,
pub version: String,
pub uptime_seconds: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadyResponse {
pub ready: bool,
pub status: ReadinessStatus,
pub version: String,
pub checks: Vec<ReadinessCheck>,
}
impl ReadyResponse {
pub fn from_checks(checks: Vec<ReadinessCheck>) -> Self {
let ready = checks
.iter()
.all(|check| check.status == ReadinessCheckStatus::Pass);
Self {
ready,
status: if ready {
ReadinessStatus::Ready
} else {
ReadinessStatus::NotReady
},
version: env!("CARGO_PKG_VERSION").to_string(),
checks,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ReadinessStatus {
Ready,
NotReady,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ReadinessCheck {
pub name: String,
pub status: ReadinessCheckStatus,
pub message: String,
}
impl ReadinessCheck {
pub fn pass(name: impl Into<String>, message: impl Into<String>) -> Self {
Self {
name: name.into(),
status: ReadinessCheckStatus::Pass,
message: message.into(),
}
}
pub fn fail(name: impl Into<String>, message: impl Into<String>) -> Self {
Self {
name: name.into(),
status: ReadinessCheckStatus::Fail,
message: message.into(),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ReadinessCheckStatus {
Pass,
Fail,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum HealthStatus {
Healthy,
Degraded,
Unhealthy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParseRequest {
pub message: String,
#[serde(default)]
pub mllp_framed: bool,
#[serde(default)]
pub options: ParseOptions,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ParseOptions {
#[serde(default = "default_true")]
pub include_json: bool,
#[serde(default = "default_true")]
pub validate_structure: bool,
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParseResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<serde_json::Value>,
pub metadata: MessageMetadata,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageMetadata {
pub message_type: String,
pub version: String,
pub sending_application: String,
pub sending_facility: String,
pub message_control_id: String,
pub segment_count: usize,
pub charsets: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidateRequest {
pub message: String,
pub profile: String,
#[serde(default)]
pub mllp_framed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidateResponse {
pub valid: bool,
pub message_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub profile: Option<String>,
pub segment_count: usize,
pub issue_count: usize,
pub issues: Vec<ValidationReportIssue>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub errors: Vec<ValidationError>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub warnings: Vec<ValidationWarning>,
pub metadata: MessageMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidateRedactedRequest {
pub message: String,
pub profile: String,
pub redaction_policy: String,
#[serde(default)]
pub mllp_framed: bool,
#[serde(default)]
pub include_redacted_hl7: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidateRedactedResponse {
pub validation_report: ValidationReport,
pub redaction_receipt: RedactionReceipt,
#[serde(skip_serializing_if = "Option::is_none")]
pub quarantine: Option<QuarantineOutputSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redacted_hl7: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BundleRequest {
pub message: String,
pub profile: String,
pub redaction_policy: String,
pub bundle_id: String,
#[serde(default)]
pub mllp_framed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceBundleSummary {
pub bundle_version: String,
pub output_dir: String,
pub message_type: String,
pub validation_valid: bool,
pub validation_issue_count: usize,
pub redaction_phi_removed: bool,
pub artifacts: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct QuarantineConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub path: Option<PathBuf>,
#[serde(default = "default_true")]
pub write_redacted: bool,
#[serde(default = "default_true")]
pub write_report: bool,
#[serde(default = "default_true")]
pub write_bundle: bool,
}
impl Default for QuarantineConfig {
fn default() -> Self {
Self {
enabled: false,
path: None,
write_redacted: true,
write_report: true,
write_bundle: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PublicQuarantineConfig {
pub enabled: bool,
pub path_configured: bool,
pub write_redacted: bool,
pub write_report: bool,
pub write_bundle: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuarantineOutputSummary {
pub quarantine_version: String,
pub output_dir: String,
pub reason: QuarantineReason,
pub validation_issue_count: usize,
pub artifacts: Vec<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum QuarantineReason {
ValidationError,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceBundleManifest {
pub bundle_version: String,
pub tool_name: String,
pub tool_version: String,
pub artifacts: Vec<EvidenceBundleManifestArtifact>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceBundleManifestArtifact {
pub path: String,
pub role: String,
pub sha256: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceBundleEnvironment {
pub bundle_version: String,
pub tool_name: String,
pub tool_version: String,
pub message_type: String,
pub input_sha256: String,
pub profile_sha256: String,
pub redaction_policy_sha256: String,
pub validation_valid: bool,
pub validation_issue_count: usize,
pub replay_command: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldPathTraceReport {
pub message_type: String,
pub field_count: usize,
pub fields: Vec<FieldPathTrace>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldPathTrace {
pub path: String,
pub canonical_path: String,
pub segment_index: usize,
pub field_index: usize,
pub present: bool,
pub value_shape: FieldValueShape,
#[serde(skip_serializing_if = "Option::is_none")]
pub redaction_action: Option<RedactionAction>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum FieldValueShape {
Empty,
Present,
HashedSha256,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RedactionReceipt {
pub phi_removed: bool,
pub hash_algorithm: String,
pub actions: Vec<RedactionActionReceipt>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RedactionActionReceipt {
pub path: String,
pub action: RedactionAction,
pub reason: String,
pub matched_count: usize,
pub optional: bool,
pub status: RedactionActionStatus,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum RedactionAction {
Hash,
Drop,
Retain,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RedactionActionStatus {
Applied,
Retained,
NotFound,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AckRequest {
pub message: String,
pub code: AckRequestCode,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
#[serde(default)]
pub mllp_framed: bool,
#[serde(default)]
pub mllp_frame: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum AckRequestCode {
#[serde(rename = "AA")]
Aa,
#[serde(rename = "AE")]
Ae,
#[serde(rename = "AR")]
Ar,
#[serde(rename = "CA")]
Ca,
#[serde(rename = "CE")]
Ce,
#[serde(rename = "CR")]
Cr,
}
impl AckRequestCode {
pub fn as_str(self) -> &'static str {
match self {
Self::Aa => "AA",
Self::Ae => "AE",
Self::Ar => "AR",
Self::Ca => "CA",
Self::Ce => "CE",
Self::Cr => "CR",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AckResponse {
pub ack_message: String,
pub ack_code: String,
pub metadata: MessageMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AckPolicyConfig {
#[serde(default)]
pub mode: AckPolicyMode,
#[serde(default)]
pub accept_on: AckPolicyAcceptOn,
#[serde(default = "default_ack_reject_on")]
pub reject_on: Vec<AckPolicyRejectCondition>,
#[serde(default = "default_include_error_text")]
pub include_error_text: bool,
}
impl Default for AckPolicyConfig {
fn default() -> Self {
Self {
mode: AckPolicyMode::Original,
accept_on: AckPolicyAcceptOn::Valid,
reject_on: default_ack_reject_on(),
include_error_text: true,
}
}
}
impl AckPolicyConfig {
pub fn rejects(&self, condition: AckPolicyRejectCondition) -> bool {
self.reject_on.contains(&condition)
}
}
fn default_ack_reject_on() -> Vec<AckPolicyRejectCondition> {
vec![
AckPolicyRejectCondition::ParseError,
AckPolicyRejectCondition::ValidationError,
]
}
fn default_include_error_text() -> bool {
true
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AckPolicyMode {
#[default]
Original,
Enhanced,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AckPolicyAcceptOn {
#[default]
Valid,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AckPolicyRejectCondition {
ParseError,
ValidationError,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AckPolicyRequest {
pub message: String,
pub profile: String,
#[serde(default)]
pub mllp_framed: bool,
#[serde(default)]
pub mllp_frame: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AckPolicyResponse {
pub ack_message: String,
pub ack_code: String,
pub decision: AckPolicyDecision,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation_report: Option<hl7v2::ValidationReport>,
pub metadata: MessageMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AckPolicyDecision {
pub mode: AckPolicyMode,
pub outcome: AckPolicyOutcome,
pub reason: AckPolicyReason,
pub ack_code: String,
pub include_error_text: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_text: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AckPolicyOutcome {
Accepted,
Rejected,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AckPolicyReason {
Valid,
ParseError,
ValidationError,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizeRequest {
pub message: String,
#[serde(default)]
pub mllp_framed: bool,
#[serde(default)]
pub options: NormalizeOptions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizeOptions {
#[serde(default = "default_true")]
pub canonical_delimiters: bool,
#[serde(default)]
pub mllp_frame: bool,
}
impl Default for NormalizeOptions {
fn default() -> Self {
Self {
canonical_delimiters: true,
mllp_frame: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizeResponse {
pub normalized_message: String,
pub metadata: MessageMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
pub severity: ErrorSeverity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationWarning {
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ErrorSeverity {
Error,
Warning,
Info,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<serde_json::Value>,
}
impl ErrorResponse {
pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
code: code.into(),
message: message.into(),
details: None,
}
}
pub fn with_details(mut self, details: serde_json::Value) -> Self {
self.details = Some(details);
self
}
}