///|
pub(all) struct SchemaField {
path : String
type_name : String
required : Bool
minimum_items : Int
description : String
} derive(Debug, Eq, ToJson)
///|
pub(all) struct ResourceSchema {
resource_type : String
version : FhirVersion
fields : Array[SchemaField]
summary : String
} derive(Debug, Eq, ToJson)
///|
fn schema_field(
path : String,
type_name : String,
required : Bool,
minimum_items : Int,
description : String,
) -> SchemaField {
{ path, type_name, required, minimum_items, description }
}
///|
pub fn schema_for(kind : String, version? : FhirVersion = R4) -> ResourceSchema {
let fields = match kind {
"Observation" =>
[
schema_field(
"resourceType", "string", true, 0, "FHIR resource discriminator",
),
schema_field("id", "string", false, 0, "logical resource id"),
schema_field(
"status", "string", true, 0, "observation lifecycle status",
),
schema_field("code", "object", true, 0, "observation code"),
schema_field("subject", "object", false, 0, "subject reference"),
]
"Patient" =>
[
schema_field(
"resourceType", "string", true, 0, "FHIR resource discriminator",
),
schema_field("id", "string", false, 0, "logical resource id"),
schema_field("name", "array", false, 0, "human names"),
schema_field("gender", "string", false, 0, "administrative gender"),
schema_field("birthDate", "string", false, 0, "birth date"),
]
"Bundle" =>
[
schema_field(
"resourceType", "string", true, 0, "FHIR resource discriminator",
),
schema_field("type", "string", true, 0, "bundle type"),
schema_field("entry", "array", true, 1, "bundle entries"),
schema_field("id", "string", false, 0, "logical resource id"),
schema_field("timestamp", "string", false, 0, "bundle timestamp"),
]
"Condition" =>
[
schema_field(
"resourceType", "string", true, 0, "FHIR resource discriminator",
),
schema_field("id", "string", false, 0, "logical resource id"),
schema_field("clinicalStatus", "object", false, 0, "clinical status"),
schema_field(
"verificationStatus", "object", false, 0, "verification status",
),
schema_field("code", "object", true, 0, "condition code"),
]
_ =>
[
schema_field(
"resourceType", "string", true, 0, "FHIR resource discriminator",
),
schema_field("id", "string", false, 0, "logical resource id"),
schema_field("status", "string", false, 0, "resource status"),
schema_field("subject", "object", false, 0, "subject reference"),
schema_field("text", "object", false, 0, "human narrative"),
]
}
{
resource_type: kind,
version,
fields,
summary: "lightweight \{kind} schema for integration validation",
}
}
///|
pub fn schema_field_count(schema : ResourceSchema) -> Int {
schema.fields.length()
}
///|
pub fn schema_field_names(schema : ResourceSchema) -> Array[String] {
schema.fields.map(fn(field) { field.path })
}
///|
fn schema_type_matches(value : Json, type_name : String) -> Bool {
match type_name {
"object" => value is Object(_)
"array" => value is Array(_)
"string" => value is String(_)
"number" => value is Number(_, ..)
"boolean" =>
match value {
True | False => true
_ => false
}
_ => true
}
}
///|
pub fn schema_validate(value : Json, schema : ResourceSchema) -> Array[Issue] {
let issues : Array[Issue] = []
for field in schema.fields {
match query_first(value, field.path) {
None =>
if field.required {
issues.push(
issue(
"/\{field.path}",
"required",
"\{field.path} is required by schema",
),
)
}
Some(actual) => {
if !schema_type_matches(actual, field.type_name) {
issues.push(
issue(
"/\{field.path}",
"type",
"\{field.path} must be a \{field.type_name}",
),
)
}
match actual {
Array(items) =>
if items.length() < field.minimum_items {
issues.push(
issue(
"/\{field.path}",
"empty",
"\{field.path} has too few items",
),
)
}
_ => ()
}
}
}
}
issues
}
///|
pub fn schema_is_valid(value : Json, schema : ResourceSchema) -> Bool {
schema_validate(value, schema).length() == 0
}
///|
pub fn schema_registry() -> Map[String, ResourceSchema] {
Map([
("Patient", schema_for("Patient")),
("Observation", schema_for("Observation")),
("Bundle", schema_for("Bundle")),
("Condition", schema_for("Condition")),
])
}
///|
pub fn schema_for_resource(value : Json) -> ResourceSchema {
schema_for(resource_type(value).unwrap_or(""))
}
///|
pub fn schema_validate_resource(value : Json) -> Array[Issue] {
schema_validate(value, schema_for_resource(value))
}
///|
pub fn schema_summary(schema : ResourceSchema) -> String {
"\{schema.resource_type} \{schema.version.to_string()} fields=\{schema.fields.length()}"
}