use std::fmt;
use std::ops::Deref;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
macro_rules! variant_string {
(
$(#[$meta:meta])*
$name:ident wraps $inner:path,
expecting $expecting:literal,
variants { $( $variant:ident => $text:literal ),+ $(,)? }
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct $name(pub $inner);
impl From<$inner> for $name {
fn from(inner: $inner) -> $name {
$name(inner)
}
}
impl From<$name> for $inner {
fn from(outer: $name) -> $inner {
outer.0
}
}
impl Deref for $name {
type Target = $inner;
fn deref(&self) -> &$inner {
&self.0
}
}
impl $name {
pub const NAMES: &'static [&'static str] = &[$($text),+];
#[must_use]
pub fn as_str(self) -> &'static str {
match self.0 {
$( <$inner>::$variant => $text, )+
}
}
#[must_use]
pub fn parse(text: &str) -> Option<$name> {
match text {
$( $text => Some($name(<$inner>::$variant)), )+
_ => None,
}
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl Visitor<'_> for V {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str($expecting)
}
fn visit_str<E>(self, value: &str) -> Result<$name, E>
where
E: de::Error,
{
$name::parse(value)
.ok_or_else(|| de::Error::unknown_variant(value, $name::NAMES))
}
}
deserializer.deserialize_str(V)
}
}
};
}
variant_string! {
NodeKind wraps hl7_2::generic::Kind,
expecting "one of \"Group\", \"Segment\", \"Field\", \"Component\", \"Subcomponent\"",
variants {
Group => "Group",
Segment => "Segment",
Field => "Field",
Component => "Component",
Subcomponent => "Subcomponent",
}
}
variant_string! {
Severity wraps hl7_2::Severity,
expecting "\"Error\" or \"Warning\"",
variants {
Error => "Error",
Warning => "Warning",
}
}
variant_string! {
DiagnosticKind wraps hl7_2::validate::Kind,
expecting "a diagnostic kind such as \"SegmentMissing\" or \"ValueFormat\"",
variants {
Header => "Header",
StructureUnknown => "StructureUnknown",
StructureMismatch => "StructureMismatch",
SegmentMissing => "SegmentMissing",
SegmentUnknown => "SegmentUnknown",
FieldUnknown => "FieldUnknown",
ComponentUnknown => "ComponentUnknown",
ValueFormat => "ValueFormat",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_kind_round_trips_every_variant() {
for text in NodeKind::NAMES {
let kind = NodeKind::parse(text).unwrap();
let json = serde_json::to_string(&kind).unwrap();
assert_eq!(json, format!("\"{text}\""));
let back: NodeKind = serde_json::from_str(&json).unwrap();
assert_eq!(back, kind);
}
}
#[test]
fn severity_round_trips_every_variant() {
for text in Severity::NAMES {
let severity = Severity::parse(text).unwrap();
let back: Severity =
serde_json::from_str(&serde_json::to_string(&severity).unwrap()).unwrap();
assert_eq!(back, severity);
}
}
#[test]
fn diagnostic_kind_round_trips_every_variant() {
for text in DiagnosticKind::NAMES {
let kind = DiagnosticKind::parse(text).unwrap();
let back: DiagnosticKind =
serde_json::from_str(&serde_json::to_string(&kind).unwrap()).unwrap();
assert_eq!(back, kind);
}
}
#[test]
fn rejects_an_unknown_variant() {
let err = serde_json::from_str::<NodeKind>(r#""Repetition""#).unwrap_err();
assert!(err.to_string().contains("Repetition"), "{err}");
let err = serde_json::from_str::<Severity>(r#""Fatal""#).unwrap_err();
assert!(err.to_string().contains("Fatal"), "{err}");
let err = serde_json::from_str::<DiagnosticKind>(r#""Typo""#).unwrap_err();
assert!(err.to_string().contains("Typo"), "{err}");
}
#[test]
fn a_variant_is_written_as_a_string_not_an_index() {
let value = serde_json::to_value(NodeKind(hl7_2::generic::Kind::Group)).unwrap();
assert!(value.is_string());
}
}