use crate::core::ir::EnumDef;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SerdeEnumRepr {
External,
Internal { tag: String },
Adjacent { tag: String, content: String },
Untagged,
}
impl SerdeEnumRepr {
pub fn kind(&self) -> &'static str {
match self {
Self::External => "external",
Self::Internal { .. } => "internal",
Self::Adjacent { .. } => "adjacent",
Self::Untagged => "untagged",
}
}
pub fn tag(&self) -> Option<&str> {
match self {
Self::Internal { tag } | Self::Adjacent { tag, .. } => Some(tag),
Self::External | Self::Untagged => None,
}
}
pub fn content(&self) -> Option<&str> {
match self {
Self::Adjacent { content, .. } => Some(content),
Self::External | Self::Internal { .. } | Self::Untagged => None,
}
}
}
pub fn serde_enum_repr(enum_def: &EnumDef) -> SerdeEnumRepr {
if enum_def.serde_untagged {
return SerdeEnumRepr::Untagged;
}
match (enum_def.serde_tag.as_deref(), enum_def.serde_content.as_deref()) {
(Some(tag), Some(content)) => SerdeEnumRepr::Adjacent {
tag: tag.to_string(),
content: content.to_string(),
},
(Some(tag), None) => SerdeEnumRepr::Internal { tag: tag.to_string() },
(None, _) => SerdeEnumRepr::External,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn enum_def(tag: Option<&str>, content: Option<&str>, untagged: bool) -> EnumDef {
EnumDef {
name: "Sample".to_string(),
serde_tag: tag.map(str::to_string),
serde_content: content.map(str::to_string),
serde_untagged: untagged,
..EnumDef::default()
}
}
#[test]
fn should_be_external_when_no_serde_container_attribute_is_present() {
let repr = serde_enum_repr(&enum_def(None, None, false));
assert_eq!(repr, SerdeEnumRepr::External);
assert_eq!(repr.kind(), "external");
assert_eq!(repr.tag(), None);
assert_eq!(repr.content(), None);
}
#[test]
fn should_be_internal_when_only_tag_is_present() {
let repr = serde_enum_repr(&enum_def(Some("type"), None, false));
assert_eq!(
repr,
SerdeEnumRepr::Internal {
tag: "type".to_string()
}
);
assert_eq!(repr.kind(), "internal");
assert_eq!(repr.tag(), Some("type"));
assert_eq!(repr.content(), None);
}
#[test]
fn should_be_adjacent_when_tag_and_content_are_both_present() {
let repr = serde_enum_repr(&enum_def(Some("kind"), Some("payload"), false));
assert_eq!(
repr,
SerdeEnumRepr::Adjacent {
tag: "kind".to_string(),
content: "payload".to_string()
}
);
assert_eq!(repr.kind(), "adjacent");
assert_eq!(repr.tag(), Some("kind"));
assert_eq!(repr.content(), Some("payload"));
}
#[test]
fn should_be_untagged_when_untagged_is_set() {
let repr = serde_enum_repr(&enum_def(None, None, true));
assert_eq!(repr, SerdeEnumRepr::Untagged);
assert_eq!(repr.kind(), "untagged");
assert_eq!(repr.tag(), None);
}
#[test]
fn should_prefer_untagged_over_a_stray_tag() {
assert_eq!(
serde_enum_repr(&enum_def(Some("type"), None, true)),
SerdeEnumRepr::Untagged
);
}
#[test]
fn should_ignore_content_without_a_tag() {
assert_eq!(
serde_enum_repr(&enum_def(None, Some("payload"), false)),
SerdeEnumRepr::External
);
}
}