use serde::de::{self, Deserializer, MapAccess, Visitor};
use serde::ser::{SerializeMap, Serializer};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StructureMap {
#[serde(default = "resource_type")]
pub resource_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub contained: Vec<Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extension: Vec<Extension>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub experimental: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub publisher: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub purpose: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub copyright: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub structure: Vec<Structure>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub import: Vec<String>,
#[serde(rename = "const", default, skip_serializing_if = "Vec::is_empty")]
pub constants: Vec<Constant>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub group: Vec<Group>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
fn resource_type() -> String {
"StructureMap".to_owned()
}
impl StructureMap {
pub fn new() -> Self {
Self {
resource_type: resource_type(),
..Self::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Extension {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_string: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
impl Extension {
pub fn string(url: &str, value: impl Into<String>) -> Self {
Self {
url: url.to_owned(),
value_string: Some(value.into()),
other: Map::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Structure {
pub url: String,
pub mode: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub alias: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Constant {
pub name: String,
pub value: String,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Group {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub extends: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub type_mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub input: Vec<Input>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rule: Vec<Rule>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Input {
pub name: String,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_name: Option<String>,
pub mode: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rule {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub source: Vec<Source>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub target: Vec<Target>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rule: Vec<Rule>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dependent: Vec<Dependent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Source {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extension: Vec<Extension>,
pub context: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub element: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list_mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variable: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub check: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub log_message: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Target {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extension: Vec<Extension>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub element: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variable: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub list_mode: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list_rule_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transform: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameter: Vec<Parameter>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Dependent {
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameter: Vec<Parameter>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Parameter {
pub value: ParameterValue,
pub value_element: Option<Value>,
}
impl From<ParameterValue> for Parameter {
fn from(value: ParameterValue) -> Self {
Self {
value,
value_element: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ParameterValue {
Id(String),
String(String),
Boolean(bool),
Integer(i64),
Decimal(serde_json::Number),
Date(String),
Time(String),
DateTime(String),
}
impl ParameterValue {
fn key(&self) -> &'static str {
match self {
ParameterValue::Id(_) => "valueId",
ParameterValue::String(_) => "valueString",
ParameterValue::Boolean(_) => "valueBoolean",
ParameterValue::Integer(_) => "valueInteger",
ParameterValue::Decimal(_) => "valueDecimal",
ParameterValue::Date(_) => "valueDate",
ParameterValue::Time(_) => "valueTime",
ParameterValue::DateTime(_) => "valueDateTime",
}
}
fn json(&self) -> Value {
match self {
ParameterValue::Id(v)
| ParameterValue::String(v)
| ParameterValue::Date(v)
| ParameterValue::Time(v)
| ParameterValue::DateTime(v) => Value::String(v.clone()),
ParameterValue::Boolean(v) => Value::Bool(*v),
ParameterValue::Integer(v) => Value::from(*v),
ParameterValue::Decimal(v) => Value::Number(v.clone()),
}
}
fn from_json(key: &str, value: Value) -> Result<Option<Self>, String> {
let text = || {
value
.as_str()
.map(str::to_owned)
.ok_or_else(|| format!("{key} must be a string"))
};
Ok(Some(match key {
"valueId" => ParameterValue::Id(text()?),
"valueString" => ParameterValue::String(text()?),
"valueDate" => ParameterValue::Date(text()?),
"valueTime" => ParameterValue::Time(text()?),
"valueDateTime" => ParameterValue::DateTime(text()?),
"valueBoolean" => {
ParameterValue::Boolean(value.as_bool().ok_or("valueBoolean must be a boolean")?)
}
"valueInteger" => {
ParameterValue::Integer(value.as_i64().ok_or("valueInteger must be an integer")?)
}
"valueDecimal" => match value {
Value::Number(n) => ParameterValue::Decimal(n),
_ => return Err("valueDecimal must be a number".to_owned()),
},
_ => return Ok(None),
}))
}
}
impl Serialize for Parameter {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let key = self.value.key();
let mut map = serializer.serialize_map(None)?;
map.serialize_entry(key, &self.value.json())?;
if let Some(element) = &self.value_element {
map.serialize_entry(&format!("_{key}"), element)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for Parameter {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct ParameterVisitor;
impl<'de> Visitor<'de> for ParameterVisitor {
type Value = Parameter;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a StructureMap parameter with one value[x] element")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Parameter, A::Error> {
let mut value = None;
let mut value_element = None;
while let Some(key) = map.next_key::<String>()? {
let json: Value = map.next_value()?;
if key.starts_with("_value") {
value_element = Some(json);
} else if key.starts_with("value") {
value = Some(
ParameterValue::from_json(&key, json)
.map_err(de::Error::custom)?
.ok_or_else(|| {
de::Error::custom(format!("unsupported parameter type {key}"))
})?,
);
}
}
let value = value.ok_or_else(|| de::Error::custom("parameter has no value[x]"))?;
Ok(Parameter {
value,
value_element,
})
}
}
deserializer.deserialize_map(ParameterVisitor)
}
}