use std::fmt;
use serde_json::{Value, json};
use fhir::FhirResource;
#[derive(Debug, Clone)]
pub struct ValidationError {
pub message: String,
pub pointer: String,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.pointer, self.message)
}
}
pub fn validate_fhir_payload(data: &str) -> Result<Vec<Value>, Vec<ValidationError>> {
let parsed: Value = serde_json::from_str(data)
.map_err(|e| vec![ValidationError {
message: format!("Invalid JSON: {}", e),
pointer: "#".to_string(),
}])?;
let mut errors: Vec<ValidationError> = Vec::new();
if parsed.get("resourceType").is_none() {
errors.push(ValidationError {
message: "Missing top-level 'resourceType' field".to_string(),
pointer: "#".to_string(),
});
return Err(errors);
}
let resource_type = parsed["resourceType"].as_str().unwrap_or("");
if resource_type == "Bundle" {
validate_bundle(&parsed, &mut errors);
} else {
validate_single_resource(&parsed, &mut errors);
}
if !errors.is_empty() {
return Err(errors);
}
Ok(vec![parsed])
}
fn validate_bundle(bundle: &Value, errors: &mut Vec<ValidationError>) {
if bundle.get("type").is_none() {
errors.push(ValidationError {
message: "Bundle must have a 'type' field (e.g., 'collection', 'transaction')".to_string(),
pointer: "#/type".to_string(),
});
}
let entries = match bundle.get("entry") {
Some(entries) => entries,
None => {
errors.push(ValidationError {
message: "Bundle must have an 'entry' array".to_string(),
pointer: "#/entry".to_string(),
});
return;
}
};
let entries = match entries.as_array() {
Some(arr