Skip to main content

serde_er7/
terminator.rs

1//! [`Terminator`]: a three-variant enum, serialized as its Rust identifier.
2
3use std::fmt;
4
5use serde::de::{self, Visitor};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8/// A Serde-enabled [`er7::Terminator`].
9///
10/// [`er7::Terminator`] is a render option, not part of a message's own
11/// data, but it is a public [`Copy`] enum with no fields, so it is included
12/// here for completeness and as the crate's one example of the "simple
13/// C-like enum" case in [serde's manual-implementation
14/// guide](https://docs.rs/serde/latest/serde/).
15///
16/// It serializes as one of the strings `"Cr"`, `"Lf"`, `"CrLf"` — the
17/// variant's own Rust identifier — via [`Serializer::serialize_str`] rather
18/// than [`Serializer::serialize_unit_variant`]. That trades the compact
19/// index a binary format could use for a value that reads the same way in
20/// every format, including the JSON and YAML this crate is mainly built
21/// for, where a terminator choice is never on a hot path.
22///
23/// Example:
24///
25/// ```
26/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
27/// use serde_er7::Terminator;
28///
29/// assert_eq!(serde_json::to_string(&Terminator(er7::Terminator::Lf))?, r#""Lf""#);
30///
31/// let back: Terminator = serde_json::from_str(r#""CrLf""#)?;
32/// assert_eq!(back.0, er7::Terminator::CrLf);
33///
34/// assert!(serde_json::from_str::<Terminator>(r#""Sixteen""#).is_err());
35/// # Ok(())
36/// # }
37/// ```
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub struct Terminator(pub er7::Terminator);
40
41impl From<er7::Terminator> for Terminator {
42    fn from(inner: er7::Terminator) -> Terminator {
43        Terminator(inner)
44    }
45}
46
47impl From<Terminator> for er7::Terminator {
48    fn from(outer: Terminator) -> er7::Terminator {
49        outer.0
50    }
51}
52
53impl Serialize for Terminator {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        let name = match self.0 {
59            er7::Terminator::Cr => "Cr",
60            er7::Terminator::Lf => "Lf",
61            er7::Terminator::CrLf => "CrLf",
62        };
63        serializer.serialize_str(name)
64    }
65}
66
67struct TerminatorVisitor;
68
69impl Visitor<'_> for TerminatorVisitor {
70    type Value = Terminator;
71
72    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
73        formatter.write_str(r#""Cr", "Lf", or "CrLf""#)
74    }
75
76    fn visit_str<E>(self, value: &str) -> Result<Terminator, E>
77    where
78        E: de::Error,
79    {
80        match value {
81            "Cr" => Ok(Terminator(er7::Terminator::Cr)),
82            "Lf" => Ok(Terminator(er7::Terminator::Lf)),
83            "CrLf" => Ok(Terminator(er7::Terminator::CrLf)),
84            other => Err(de::Error::unknown_variant(other, &["Cr", "Lf", "CrLf"])),
85        }
86    }
87}
88
89impl<'de> Deserialize<'de> for Terminator {
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: Deserializer<'de>,
93    {
94        deserializer.deserialize_str(TerminatorVisitor)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn round_trips_every_variant() {
104        for terminator in [
105            er7::Terminator::Cr,
106            er7::Terminator::Lf,
107            er7::Terminator::CrLf,
108        ] {
109            let wrapped = Terminator(terminator);
110            let json = serde_json::to_string(&wrapped).unwrap();
111            let back: Terminator = serde_json::from_str(&json).unwrap();
112            assert_eq!(back.0, terminator);
113        }
114    }
115
116    #[test]
117    fn rejects_an_unknown_variant() {
118        assert!(serde_json::from_str::<Terminator>(r#""Sixteen""#).is_err());
119    }
120}