#![expect(
clippy::disallowed_types,
reason = "canonical JSON / Simplified Formats operate on the wire value by definition \
(ITS-JSON; ITS-REST Simplified Formats) — serde_json::Value IS the subject matter \
(#1694)"
)]
use std::collections::BTreeMap;
use indexmap::IndexMap;
use serde::Serialize;
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplate {
#[serde(rename = "templateId")]
pub template_id: String,
#[serde(rename = "semVer", skip_serializing_if = "Option::is_none")]
pub sem_ver: Option<String>,
pub version: String,
#[serde(rename = "defaultLanguage")]
pub default_language: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub languages: Vec<String>,
#[serde(serialize_with = "super::root::serialize_root")]
pub tree: WebTemplateNode,
#[serde(rename = "otherDetails", skip_serializing_if = "IndexMap::is_empty")]
pub other_details: IndexMap<String, String>,
#[serde(skip)]
pub archetype_refs: Option<BTreeMap<String, String>>,
}
#[derive(Debug, Clone)]
pub struct CodedName {
pub terminology: String,
pub code: String,
pub incoherent: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplateNode {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "localizedName", skip_serializing_if = "Option::is_none")]
pub localized_name: Option<String>,
#[serde(rename = "rmType")]
pub rm_type: String,
#[serde(rename = "nodeId", skip_serializing_if = "Option::is_none")]
pub node_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<i32>,
pub max: i32,
#[serde(rename = "inContext", skip_serializing_if = "Option::is_none")]
pub in_context: Option<bool>,
#[serde(rename = "aqlPath")]
pub aql_path: String,
#[serde(rename = "localizedNames", skip_serializing_if = "IndexMap::is_empty")]
pub localized_names: IndexMap<String, String>,
#[serde(
rename = "localizedDescriptions",
skip_serializing_if = "IndexMap::is_empty"
)]
pub localized_descriptions: IndexMap<String, String>,
#[serde(skip_serializing_if = "IndexMap::is_empty")]
pub annotations: IndexMap<String, String>,
#[serde(rename = "termBindings", skip_serializing_if = "IndexMap::is_empty")]
pub term_bindings: IndexMap<String, WebTemplateBindingCodedValue>,
#[serde(rename = "proportionTypes", skip_serializing_if = "Vec::is_empty")]
pub proportion_types: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub inputs: Vec<WebTemplateInput>,
#[serde(rename = "dependsOn", skip_serializing_if = "Option::is_none")]
pub depends_on: Option<Vec<String>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub cardinalities: Vec<WebTemplateCardinality>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub children: Vec<WebTemplateNode>,
#[serde(skip)]
pub existence: Vec<WebTemplateExistence>,
#[serde(skip)]
pub card_all: Vec<WebTemplateCardinality>,
#[serde(skip)]
pub slots: Vec<WebTemplateSlot>,
#[serde(skip)]
pub implicit: bool,
#[serde(skip)]
pub numeric_lists: Vec<(String, Vec<f64>)>,
#[serde(skip)]
pub numeric_ranges: Vec<(String, WebTemplateRange)>,
#[serde(skip)]
pub duration_range: Option<WebTemplateRange>,
#[serde(skip)]
pub tz_validity: Option<i32>,
#[serde(skip)]
pub quantity_property: Option<String>,
#[serde(skip)]
pub coded_terminology_local: bool,
#[serde(skip)]
pub code_lists: Vec<WebTemplateCodeList>,
#[serde(skip)]
pub constraint_bindings: Vec<WebTemplateConstraintBinding>,
#[serde(skip)]
pub closed_attributes: Vec<WebTemplateClosedAttribute>,
#[serde(skip)]
pub structural_stubs: Vec<WebTemplateStructuralStub>,
#[serde(skip)]
pub alternatives: Vec<WebTemplateNode>,
#[serde(skip)]
pub full_id: String,
#[serde(skip)]
pub alt_json_id: Option<String>,
#[serde(skip)]
pub name_code: Option<String>,
#[serde(skip)]
pub name_coded: Option<CodedName>,
#[serde(skip)]
pub archetype_ref: Option<String>,
#[serde(skip)]
pub rm_release: Option<String>,
#[serde(skip)]
pub(crate) walk: Option<Box<crate::flat::validation::NodeWalk>>,
}
impl WebTemplateNode {
#[must_use]
pub fn new(rm_type: String, aql_path: String) -> Self {
Self {
id: String::new(),
name: None,
localized_name: None,
rm_type,
node_id: None,
min: None,
max: -1,
in_context: None,
aql_path,
localized_names: IndexMap::new(),
localized_descriptions: IndexMap::new(),
annotations: IndexMap::new(),
term_bindings: IndexMap::new(),
proportion_types: Vec::new(),
inputs: Vec::new(),
depends_on: None,
cardinalities: Vec::new(),
children: Vec::new(),
existence: Vec::new(),
card_all: Vec::new(),
slots: Vec::new(),
implicit: false,
numeric_lists: Vec::new(),
numeric_ranges: Vec::new(),
duration_range: None,
tz_validity: None,
quantity_property: None,
coded_terminology_local: false,
code_lists: Vec::new(),
constraint_bindings: Vec::new(),
closed_attributes: Vec::new(),
structural_stubs: Vec::new(),
alternatives: Vec::new(),
full_id: String::new(),
alt_json_id: None,
name_code: None,
name_coded: None,
archetype_ref: None,
rm_release: None,
walk: None,
}
}
pub(crate) fn has_input(&self) -> bool {
!self.inputs.is_empty()
}
pub(crate) fn first_input_type(&self) -> Option<WebTemplateInputType> {
self.inputs.first().map(|i| i.input_type)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum WebTemplateInputType {
Text,
CodedText,
Date,
Time,
Datetime,
Boolean,
Integer,
Decimal,
Duration,
Quantity,
Count,
Proportion,
}
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplateInput {
#[serde(skip_serializing_if = "Option::is_none")]
pub suffix: Option<String>,
#[serde(rename = "type")]
pub input_type: WebTemplateInputType,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub list: Vec<WebTemplateCodedValue>,
#[serde(rename = "listOpen", skip_serializing_if = "Option::is_none")]
pub list_open: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation: Option<WebTemplateValidation>,
#[serde(rename = "defaultValue", skip_serializing_if = "Option::is_none")]
pub default_value: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub terminology: Option<String>,
}
impl WebTemplateInput {
#[must_use]
pub fn new(input_type: WebTemplateInputType, suffix: Option<&str>) -> Self {
Self {
suffix: suffix.map(str::to_owned),
input_type,
list: Vec::new(),
list_open: None,
validation: None,
default_value: None,
terminology: None,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplateCodedValue {
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(rename = "localizedLabels", skip_serializing_if = "IndexMap::is_empty")]
pub localized_labels: IndexMap<String, String>,
#[serde(
rename = "localizedDescriptions",
skip_serializing_if = "IndexMap::is_empty"
)]
pub localized_descriptions: IndexMap<String, String>,
#[serde(rename = "termBindings", skip_serializing_if = "IndexMap::is_empty")]
pub term_bindings: IndexMap<String, WebTemplateBindingCodedValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub validation: Option<WebTemplateValidation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ordinal: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scale: Option<f64>,
}
impl WebTemplateCodedValue {
pub fn new(value: impl Into<String>, label: Option<String>) -> Self {
Self {
value: value.into(),
label,
localized_labels: IndexMap::new(),
localized_descriptions: IndexMap::new(),
term_bindings: IndexMap::new(),
validation: None,
ordinal: None,
scale: None,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplateBindingCodedValue {
pub value: String,
#[serde(rename = "terminologyId")]
pub terminology_id: String,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct WebTemplateValidation {
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub range: Option<WebTemplateRange>,
#[serde(skip_serializing_if = "Option::is_none")]
pub precision: Option<WebTemplateRange>,
}
impl WebTemplateValidation {
pub(crate) fn is_empty(&self) -> bool {
self.pattern.is_none() && self.range.is_none() && self.precision.is_none()
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct WebTemplateRange {
#[serde(rename = "minOp", skip_serializing_if = "Option::is_none")]
pub min_op: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<serde_json::Value>,
#[serde(rename = "maxOp", skip_serializing_if = "Option::is_none")]
pub max_op: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct WebTemplateExistence {
pub min: i32,
pub max: i32,
pub path: String,
}
#[derive(Debug, Clone)]
pub struct WebTemplateSlot {
pub path: String,
pub rm_type: String,
pub name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct WebTemplateCodeList {
pub attr: String,
pub terminology: Option<String>,
pub codes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WebTemplateConstraintBinding {
pub attr: String,
pub ac_code: String,
pub terminology: String,
pub query_uri: String,
}
#[derive(Debug, Clone)]
pub struct WebTemplateStructuralStub {
pub attr: String,
pub path: String,
pub rm_type: String,
pub node_id: String,
pub name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct WebTemplateClosedAttribute {
pub path: String,
pub allowed_ids: Vec<String>,
pub allowed_rm_types: Vec<String>,
pub slots: Vec<WebTemplateArchetypeSlot>,
}
#[derive(Debug, Clone)]
pub struct WebTemplateArchetypeSlot {
pub rm_type: String,
pub min: i32,
pub max: i32,
pub includes: Vec<String>,
pub excludes: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WebTemplateCardinality {
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<i32>,
pub max: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub ids: Option<Vec<String>>,
#[serde(skip)]
pub path: String,
}