1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
//! Macros for serializing / deserializing enums containing no data variants using serde. /// Implements deserialization and serialization for an enum of the form: /// /// ``` /// # mod a { /// pub enum Color { /// Red, /// Blue, /// } /// # } /// ``` /// /// to serialize `Color::Red` to `"red"`. This overrides serde's default behavior of `{"Red":[]}`. /// One must pass an identifier to use as the type's Visitor. /// /// Relies on the type implementing `FromStr` to call `parse` when deserializing. /// /// Relies on `AsRef` implementation when serializing. /// /// /// # Example /// /// ``` /// #[macro_use] extern crate serializable_enum; /// extern crate serde; /// # fn main() { /// # mod a { /// /// // your error type /// #[derive(Debug)] /// pub enum Error { /// Parse(String), /// } /// /// // You will need display implemented (you should already have this). /// impl ::std::fmt::Display for Error { /// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { /// write!(f, "{:?}", self) /// } /// } /// /// serializable_enum! { /// /// Color /// pub enum Color { /// /// Red /// Red, /// /// Blue /// Blue, /// /// Green /// Green, /// } /// ColorVistor /// } /// /// impl_as_ref_from_str! { /// Color { /// Red => "red", /// Blue => "blue", /// Green => "green", /// } /// Error::Parse /// } /// # } } #[macro_export] macro_rules! serializable_enum { ($(#[$enum_comment:meta])* pub enum $name:ident { $($(#[$enum_variant_comment:meta])+ $variant:ident,)+ } $visitor:ident) => ( $(#[$enum_comment])* #[derive(Debug, PartialEq, Clone)] pub enum $name { $($(#[$enum_variant_comment])+ $variant,)+ } impl ::serde::ser::Serialize for $name { fn serialize<S>(&self, serializer: &mut S) -> ::std::result::Result<(), S::Error> where S: ::serde::Serializer { self.as_ref().serialize(serializer) } } struct $visitor; impl ::serde::de::Visitor for $visitor { type Value = $name; fn visit_str<E>(&mut self, s: &str) -> ::std::result::Result<Self::Value, E> where E: ::serde::de::Error, { match s.trim().parse::<$name>() { Ok(t) => Ok(t), Err(e) => Err(::serde::de::Error::unknown_field(&e.to_string()[..])), } } } impl ::serde::Deserialize for $name { fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<$name, D::Error> where D: ::serde::Deserializer, { deserializer.visit_str($visitor) } } ) } /// Generate `AsRef` and `FromStr` impls for the given type with the variant / string pairs /// specified. /// /// # Example /// /// ``` /// #[macro_use] extern crate serializable_enum; /// # fn main() { mod a { /// /// // your error type /// #[derive(Debug)] /// enum Error { /// Parse(String), /// } /// /// // You will need display implemented (you should already have this). /// impl ::std::fmt::Display for Error { /// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { /// write!(f, "{:?}", self) /// } /// } /// /// enum Color { /// /// Red /// Red, /// /// Blue /// Blue, /// /// Green /// Green, /// } /// /// impl_as_ref_from_str! { /// Color { /// Red => "red", /// Blue => "blue", /// Green => "green", /// } /// Error::Parse /// } /// # } } #[macro_export] macro_rules! impl_as_ref_from_str { ($name:ident { $($variant:ident => $str:expr,)+ } $err:ident::$err_variant:ident ) => ( impl AsRef<str> for $name { fn as_ref(&self) -> &str { match *self { $($name::$variant=> $str,)+ } } } impl ::std::str::FromStr for $name { type Err = $err; fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> { match s { $($str => Ok($name::$variant),)+ _ => Err($err::$err_variant(format!("`{}` is not a known `{}` variant", s, stringify!($name)))), } } } ) }