use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[non_exhaustive]
pub struct Id(String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[non_exhaustive]
pub struct UTCDate(String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[non_exhaustive]
pub struct Date(String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[non_exhaustive]
pub struct State(String);
macro_rules! impl_string_newtype {
($T:ident) => {
impl fmt::Display for $T {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl From<String> for $T {
fn from(s: String) -> Self {
Self(s)
}
}
impl From<&str> for $T {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
impl AsRef<str> for $T {
fn as_ref(&self) -> &str {
&self.0
}
}
impl PartialEq<str> for $T {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<&str> for $T {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl std::borrow::Borrow<str> for $T {
fn borrow(&self) -> &str {
&self.0
}
}
impl $T {
pub fn into_inner(self) -> String {
self.0
}
}
};
}
impl_string_newtype!(Id);
impl_string_newtype!(UTCDate);
impl_string_newtype!(Date);
impl_string_newtype!(State);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn id_serializes_as_plain_string() {
let id = Id("abc123".to_owned());
let json = serde_json::to_string(&id).expect("serialize Id");
assert_eq!(json, "\"abc123\"");
}
#[test]
fn id_deserializes_from_plain_string() {
let id: Id = serde_json::from_str("\"abc123\"").expect("deserialize Id");
assert_eq!(id.as_ref(), "abc123");
}
#[test]
fn utcdate_serializes_as_plain_string() {
let d = UTCDate("2014-10-30T06:12:00Z".to_owned());
let json = serde_json::to_string(&d).expect("serialize UTCDate");
assert_eq!(json, "\"2014-10-30T06:12:00Z\"");
}
#[test]
fn state_serializes_as_plain_string() {
let s = State("75128aab4b1b".to_owned());
let json = serde_json::to_string(&s).expect("serialize State");
assert_eq!(json, "\"75128aab4b1b\"");
}
#[test]
fn id_from_str() {
let id = Id::from("hello");
assert_eq!(id.as_ref(), "hello");
}
#[test]
fn id_display() {
let id = Id("display-test".to_owned());
assert_eq!(id.to_string(), "display-test");
}
#[test]
fn id_as_ref_str() {
let id = Id("ref-test".to_owned());
assert_eq!(id.as_ref(), "ref-test");
}
#[test]
fn state_round_trip() {
let s = State("75128aab4b1b".to_owned());
let json = serde_json::to_string(&s).expect("serialize");
let s2: State = serde_json::from_str(&json).expect("deserialize");
assert_eq!(s, s2);
}
#[test]
fn date_accepts_non_utc_offset() {
let d = Date("2014-10-30T14:12:00+08:00".to_owned());
let json = serde_json::to_string(&d).expect("serialize Date");
assert_eq!(json, "\"2014-10-30T14:12:00+08:00\"");
let d2: Date = serde_json::from_str(&json).expect("deserialize Date");
assert_eq!(d, d2);
}
}