├── .gitignore ├── CHANGELOG ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src ├── de.rs ├── error.rs ├── lib.rs ├── macros.rs └── ser.rs └── tests ├── test_de.rs ├── test_macros.rs └── test_ser.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # 1.0.2 2 | 3 | - Fix a bad documentation comment. #7 4 | 5 | # 1.0.1 6 | 7 | - Fix failure on ignored fields. #5 8 | 9 | # 1.0.0 10 | 11 | - added the ability to capture lifetimes. 12 | - renamed macros but kept deprecated legacy aliases around: 13 | - `forward_from_str_to_serde!` -> `derive_fromstr_from_deserialize!` 14 | - `forward_display_to_serde!` -> `derive_display_from_serialize!` 15 | - `derive_deserialize_from_str!` -> `derive_deserialize_from_fromstr!` 16 | - modernized codebase slightly and fixed clippy warnings. 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_plain" 3 | version = "1.0.2" 4 | authors = ["Armin Ronacher "] 5 | license = "MIT/Apache-2.0" 6 | description = "A restricted plain text serializer for serde" 7 | homepage = "https://docs.rs/serde_plain" 8 | documentation = "https://docs.rs/serde_plain" 9 | repository = "https://github.com/mitsuhiko/serde-plain" 10 | keywords = ["serde", "serialization", "from_str", "display"] 11 | categories = ["encoding"] 12 | readme = "README.md" 13 | edition = "2018" 14 | 15 | [dependencies] 16 | serde = "1.0.29" 17 | 18 | [dev-dependencies] 19 | serde_derive = "1.0.29" 20 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serde Plain 2 | 3 | This crate implements a plain text serializer and deserializer. It can only 4 | serialize and deserialize primitives and derivatives thereof (like basic enums 5 | or newtypes). It internally uses the `FromStr` and `Display` trait to convert 6 | objects around. 7 | 8 | ## From String 9 | 10 | To parse a value from a string the from_str helper can be used: 11 | 12 | ```rust 13 | assert_eq!(serde_plain::from_str::("42").unwrap(), 42); 14 | ``` 15 | 16 | This is particularly useful if enums are in use: 17 | 18 | ```rust 19 | use serde::Deserialize; 20 | 21 | #[derive(Deserialize, Debug, PartialEq, Eq)] 22 | pub enum MyEnum { 23 | VariantA, 24 | VariantB, 25 | } 26 | 27 | assert_eq!(serde_plain::from_str::("VariantA").unwrap(), MyEnum::VariantA); 28 | ``` 29 | 30 | ## To String 31 | 32 | The inverse is also possible with to_string: 33 | 34 | ```rust 35 | assert_eq!(serde_plain::to_string(&true).unwrap(), "true"); 36 | ``` -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | use serde::de::{self, Deserialize, IntoDeserializer, Visitor}; 2 | 3 | use crate::error::Error; 4 | 5 | /// A simple deserializer that works with plain strings. 6 | pub struct Deserializer<'de> { 7 | input: &'de str, 8 | } 9 | 10 | impl<'de> Deserializer<'de> { 11 | pub fn new(input: &'de str) -> Self { 12 | Deserializer { input } 13 | } 14 | } 15 | 16 | /// Deserialize an instance of type `T` from a string of plain text. 17 | /// 18 | /// This deserializes the string into an object with the `Deserializer` 19 | /// and returns it. This requires that the type is a simple one 20 | /// (integer, string etc.). 21 | pub fn from_str<'a, T>(s: &'a str) -> Result 22 | where 23 | T: Deserialize<'a>, 24 | { 25 | T::deserialize(Deserializer::new(s)) 26 | } 27 | 28 | macro_rules! forward_to_deserialize_from_str { 29 | ($func:ident, $visit_func:ident, $tymsg:expr) => { 30 | fn $func(self, visitor: V) -> Result 31 | where 32 | V: Visitor<'de>, 33 | { 34 | visitor.$visit_func( 35 | self.input 36 | .parse() 37 | .map_err(|e| Error::Parse($tymsg, format!("{}", e)))?, 38 | ) 39 | } 40 | }; 41 | } 42 | 43 | impl<'de> de::Deserializer<'de> for Deserializer<'de> { 44 | type Error = Error; 45 | 46 | fn deserialize_any(self, visitor: V) -> Result 47 | where 48 | V: Visitor<'de>, 49 | { 50 | self.deserialize_str(visitor) 51 | } 52 | 53 | forward_to_deserialize_from_str!(deserialize_bool, visit_bool, "boolean"); 54 | forward_to_deserialize_from_str!(deserialize_i8, visit_i8, "i8"); 55 | forward_to_deserialize_from_str!(deserialize_i16, visit_i16, "i16"); 56 | forward_to_deserialize_from_str!(deserialize_i32, visit_i32, "i32"); 57 | forward_to_deserialize_from_str!(deserialize_i64, visit_i64, "i64"); 58 | forward_to_deserialize_from_str!(deserialize_u8, visit_u8, "u8"); 59 | forward_to_deserialize_from_str!(deserialize_u16, visit_u16, "u16"); 60 | forward_to_deserialize_from_str!(deserialize_u32, visit_u32, "u32"); 61 | forward_to_deserialize_from_str!(deserialize_u64, visit_u64, "u64"); 62 | forward_to_deserialize_from_str!(deserialize_f32, visit_f32, "f32"); 63 | forward_to_deserialize_from_str!(deserialize_f64, visit_f64, "f64"); 64 | forward_to_deserialize_from_str!(deserialize_char, visit_char, "char"); 65 | 66 | fn deserialize_str(self, visitor: V) -> Result 67 | where 68 | V: Visitor<'de>, 69 | { 70 | visitor.visit_borrowed_str(self.input) 71 | } 72 | 73 | fn deserialize_string(self, visitor: V) -> Result 74 | where 75 | V: Visitor<'de>, 76 | { 77 | self.deserialize_str(visitor) 78 | } 79 | 80 | fn deserialize_bytes(self, _visitor: V) -> Result 81 | where 82 | V: Visitor<'de>, 83 | { 84 | Err(Error::ImpossibleDeserialization("bytes")) 85 | } 86 | 87 | fn deserialize_byte_buf(self, _visitor: V) -> Result 88 | where 89 | V: Visitor<'de>, 90 | { 91 | Err(Error::ImpossibleDeserialization("bytes")) 92 | } 93 | 94 | fn deserialize_option(self, visitor: V) -> Result 95 | where 96 | V: Visitor<'de>, 97 | { 98 | if self.input.is_empty() { 99 | visitor.visit_none() 100 | } else { 101 | visitor.visit_some(self) 102 | } 103 | } 104 | 105 | fn deserialize_unit(self, visitor: V) -> Result 106 | where 107 | V: Visitor<'de>, 108 | { 109 | if self.input.is_empty() { 110 | visitor.visit_unit() 111 | } else { 112 | Err(Error::Message("expected empty string for unit".into())) 113 | } 114 | } 115 | 116 | fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result 117 | where 118 | V: Visitor<'de>, 119 | { 120 | self.deserialize_unit(visitor) 121 | } 122 | 123 | fn deserialize_newtype_struct( 124 | self, 125 | _name: &'static str, 126 | visitor: V, 127 | ) -> Result 128 | where 129 | V: Visitor<'de>, 130 | { 131 | visitor.visit_newtype_struct(self) 132 | } 133 | 134 | fn deserialize_seq(self, _visitor: V) -> Result 135 | where 136 | V: Visitor<'de>, 137 | { 138 | Err(Error::ImpossibleDeserialization("seq")) 139 | } 140 | 141 | fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result 142 | where 143 | V: Visitor<'de>, 144 | { 145 | Err(Error::ImpossibleDeserialization("tuple")) 146 | } 147 | 148 | fn deserialize_tuple_struct( 149 | self, 150 | _name: &'static str, 151 | _len: usize, 152 | _visitor: V, 153 | ) -> Result 154 | where 155 | V: Visitor<'de>, 156 | { 157 | Err(Error::ImpossibleDeserialization("tuple struct")) 158 | } 159 | 160 | fn deserialize_map(self, _visitor: V) -> Result 161 | where 162 | V: Visitor<'de>, 163 | { 164 | Err(Error::ImpossibleDeserialization("map")) 165 | } 166 | 167 | fn deserialize_struct( 168 | self, 169 | _name: &'static str, 170 | _fields: &'static [&'static str], 171 | _visitor: V, 172 | ) -> Result 173 | where 174 | V: Visitor<'de>, 175 | { 176 | Err(Error::ImpossibleDeserialization("struct")) 177 | } 178 | 179 | fn deserialize_enum( 180 | self, 181 | _name: &'static str, 182 | _variants: &'static [&'static str], 183 | visitor: V, 184 | ) -> Result 185 | where 186 | V: Visitor<'de>, 187 | { 188 | visitor.visit_enum(self.input.into_deserializer()) 189 | } 190 | 191 | fn deserialize_identifier(self, visitor: V) -> Result 192 | where 193 | V: Visitor<'de>, 194 | { 195 | self.deserialize_str(visitor) 196 | } 197 | 198 | fn deserialize_ignored_any(self, visitor: V) -> Result 199 | where 200 | V: Visitor<'de>, 201 | { 202 | self.deserialize_any(visitor) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use serde::{de, ser}; 2 | use std::fmt; 3 | 4 | use std::error; 5 | 6 | /// Errors created from this crate. 7 | #[derive(Debug, Clone)] 8 | pub enum Error { 9 | /// An impossible / unsupported operation was attempted. 10 | ImpossibleSerialization(&'static str), 11 | /// A certain deserialization is impossible. 12 | ImpossibleDeserialization(&'static str), 13 | /// Raised when parsing errors happen during deserialization. 14 | Parse(&'static str, String), 15 | /// An arbitrary error message. 16 | Message(String), 17 | } 18 | 19 | impl ser::Error for Error { 20 | fn custom(msg: T) -> Error { 21 | Error::Message(msg.to_string()) 22 | } 23 | } 24 | 25 | impl de::Error for Error { 26 | fn custom(msg: T) -> Error { 27 | Error::Message(msg.to_string()) 28 | } 29 | } 30 | 31 | impl error::Error for Error {} 32 | 33 | impl fmt::Display for Error { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | match *self { 36 | Error::ImpossibleSerialization(ty) => { 37 | write!(f, "cannot serialize non primitive type {}", ty) 38 | } 39 | Error::ImpossibleDeserialization(ty) => { 40 | write!(f, "cannot deserialize to non primitive type {}", ty) 41 | } 42 | Error::Parse(ref ty, ref msg) => write!(f, "cannot parse {}: {}", ty, msg), 43 | Error::Message(ref msg) => write!(f, "{}", msg.as_str()), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate implements a plain text serializer and deserializer. It 2 | //! can only serialize and deserialize primitives and derivatives thereof 3 | //! (like basic enums or newtypes). It internally uses the 4 | //! [`FromStr`](std::str::FromStr) and [`Display`](std::fmt::Display) trait to 5 | //! convert objects around. 6 | //! 7 | //! The idea of this crate is that you can use the serde system to implement 8 | //! [`FromStr`](std::str::FromStr) or [`Display`](std::fmt::Display) for your own 9 | //! types based on the how serde would handle the type. 10 | //! 11 | //! # From String 12 | //! 13 | //! To parse a value from a string the [`from_str`] helper can be used: 14 | //! 15 | //! ```rust 16 | //! assert_eq!(serde_plain::from_str::("42").unwrap(), 42); 17 | //! ``` 18 | //! 19 | //! This is particularly useful if enums are in use: 20 | //! 21 | //! ```rust 22 | //! # #[macro_use] extern crate serde_derive; 23 | //! use serde::Deserialize; 24 | //! 25 | //! # fn main() { 26 | //! #[derive(Deserialize, Debug, PartialEq, Eq)] 27 | //! pub enum MyEnum { 28 | //! VariantA, 29 | //! VariantB, 30 | //! } 31 | //! 32 | //! assert_eq!(serde_plain::from_str::("VariantA").unwrap(), MyEnum::VariantA); 33 | //! # } 34 | //! ``` 35 | //! 36 | //! # To String 37 | //! 38 | //! The inverse is also possible with [`to_string`]: 39 | //! 40 | //! ```rust 41 | //! assert_eq!(serde_plain::to_string(&true).unwrap(), "true"); 42 | //! ``` 43 | mod de; 44 | mod error; 45 | mod macros; 46 | mod ser; 47 | 48 | pub use crate::de::*; 49 | pub use crate::error::*; 50 | pub use crate::ser::*; 51 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | /// Implements [`FromStr`](std::str::FromStr) for a type that forwards to [`Deserialize`](serde::Deserialize). 3 | /// 4 | /// ```rust 5 | /// # #[macro_use] extern crate serde_derive; 6 | /// use serde::Deserialize; 7 | /// use serde_plain::derive_fromstr_from_deserialize; 8 | /// # fn main() { 9 | /// 10 | /// #[derive(Deserialize, Debug)] 11 | /// pub enum MyEnum { 12 | /// VariantA, 13 | /// VariantB, 14 | /// } 15 | /// 16 | /// derive_fromstr_from_deserialize!(MyEnum); 17 | /// # } 18 | /// ``` 19 | /// 20 | /// This automatically implements [`FromStr`](std::str::FromStr) which will 21 | /// invoke the [`from_str`](crate::from_str) method from this crate. 22 | /// 23 | /// Additionally this macro supports a second argument which can be the 24 | /// error type to use. In that case `From` needs 25 | /// to be implemented for that error. 26 | /// 27 | /// A third form with a conversion function as second argument is supported. 28 | /// The closure needs to be in the form `|err| -> ErrType { ... }`: 29 | /// 30 | /// ```rust 31 | /// # #[macro_use] extern crate serde_derive; 32 | /// use serde::Deserialize; 33 | /// use serde_plain::derive_fromstr_from_deserialize; 34 | /// # fn main() { 35 | /// 36 | /// #[derive(Deserialize, Debug)] 37 | /// pub enum MyEnum { 38 | /// VariantA, 39 | /// VariantB, 40 | /// } 41 | /// 42 | /// #[derive(Debug)] 43 | /// pub struct MyError(String); 44 | /// 45 | /// derive_fromstr_from_deserialize!(MyEnum, |err| -> MyError { MyError(err.to_string()) }); 46 | /// # } 47 | /// ``` 48 | macro_rules! derive_fromstr_from_deserialize { 49 | ($type:ty) => { 50 | impl ::std::str::FromStr for $type { 51 | type Err = $crate::Error; 52 | fn from_str(s: &str) -> ::std::result::Result<$type, Self::Err> { 53 | $crate::from_str(s) 54 | } 55 | } 56 | }; 57 | ($type:ty, |$var:ident| -> $err_type:ty { $err_conv:expr }) => { 58 | impl ::std::str::FromStr for $type { 59 | type Err = $err_type; 60 | fn from_str(s: &str) -> ::std::result::Result<$type, Self::Err> { 61 | $crate::from_str(s).map_err(|$var| ($err_conv)) 62 | } 63 | } 64 | }; 65 | ($type:ty, $err_type:ty) => { 66 | impl ::std::str::FromStr for $type { 67 | type Err = $err_type; 68 | fn from_str(s: &str) -> ::std::result::Result<$type, Self::Err> { 69 | $crate::from_str(s).map_err(|e| e.into()) 70 | } 71 | } 72 | }; 73 | } 74 | 75 | /// Legacy alias for [`derive_fromstr_from_deserialize`]. 76 | #[deprecated(note = "legacy alias for derive_fromstr_from_deserialize")] 77 | #[doc(hidden)] 78 | #[macro_export] 79 | macro_rules! forward_from_str_to_serde { 80 | ($($tt:tt)*) => { $crate::derive_fromstr_from_deserialize!($($tt)*); } 81 | } 82 | 83 | #[macro_export] 84 | /// Implements [`Display`](std::fmt::Display) for a type that forwards to [`Serialize`](serde::Serialize). 85 | /// 86 | /// ```rust 87 | /// # #[macro_use] extern crate serde_derive; 88 | /// use serde::Deserialize; 89 | /// use serde_plain::derive_display_from_serialize; 90 | /// # fn main() { 91 | /// 92 | /// #[derive(Serialize, Debug)] 93 | /// pub enum MyEnum { 94 | /// VariantA, 95 | /// VariantB, 96 | /// } 97 | /// 98 | /// derive_display_from_serialize!(MyEnum); 99 | /// # } 100 | /// ``` 101 | /// 102 | /// This automatically implements [`Display`](std::fmt::Display) which will 103 | /// invoke the [`to_string`](crate::to_string) method from this crate. In case 104 | /// that fails the method will panic. 105 | macro_rules! derive_display_from_serialize { 106 | ($type:ident $(:: $type_extra:ident)* < $($lt:lifetime),+ >) => { 107 | impl<$($lt,)*> ::std::fmt::Display for $type$(:: $type_extra)*<$($lt,)*> { 108 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 109 | write!(f, "{}", $crate::to_string(self).unwrap()) 110 | } 111 | } 112 | }; 113 | ($type:ty) => { 114 | impl ::std::fmt::Display for $type { 115 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 116 | write!(f, "{}", $crate::to_string(self).unwrap()) 117 | } 118 | } 119 | }; 120 | } 121 | 122 | /// Legacy alias for [`derive_display_from_serialize`]. 123 | #[deprecated(note = "legacy alias for derive_display_from_serialize")] 124 | #[doc(hidden)] 125 | #[macro_export] 126 | macro_rules! forward_display_to_serde { 127 | ($($tt:tt)*) => { $crate::derive_display_from_serialize!($($tt)*); } 128 | } 129 | 130 | /// Derives [`Deserialize`](serde::Serialize) for a type that implements [`FromStr`](std::str::FromStr). 131 | /// 132 | /// ```rust 133 | /// use std::str::FromStr; 134 | /// use std::num::ParseIntError; 135 | /// use serde_plain::derive_deserialize_from_fromstr; 136 | /// # fn main() { 137 | /// 138 | /// pub struct MyStruct(u32); 139 | /// 140 | /// impl FromStr for MyStruct { 141 | /// type Err = ParseIntError; 142 | /// fn from_str(value: &str) -> Result { 143 | /// Ok(MyStruct(value.parse()?)) 144 | /// } 145 | /// } 146 | /// 147 | /// derive_deserialize_from_fromstr!(MyStruct, "valid positive number"); 148 | /// # } 149 | /// ``` 150 | /// 151 | /// This automatically implements [`Serialize`](serde::Serialize) which will 152 | /// invoke the [`from_str`](crate::from_str) function on the target type 153 | /// internally. First argument is the name of the type, the second is a message 154 | /// for the expectation error (human readable type effectively). 155 | #[macro_export] 156 | macro_rules! derive_deserialize_from_fromstr { 157 | ($type:ty, $expectation:expr) => { 158 | impl<'de> ::serde::de::Deserialize<'de> for $type { 159 | fn deserialize(deserializer: D) -> ::std::result::Result 160 | where 161 | D: ::serde::de::Deserializer<'de>, 162 | { 163 | struct V; 164 | 165 | impl<'de> ::serde::de::Visitor<'de> for V { 166 | type Value = $type; 167 | 168 | fn expecting( 169 | &self, 170 | formatter: &mut ::std::fmt::Formatter, 171 | ) -> ::std::fmt::Result { 172 | formatter.write_str($expectation) 173 | } 174 | 175 | fn visit_str(self, value: &str) -> ::std::result::Result<$type, E> 176 | where 177 | E: ::serde::de::Error, 178 | { 179 | value.parse().map_err(|_| { 180 | ::serde::de::Error::invalid_value( 181 | ::serde::de::Unexpected::Str(value), 182 | &self, 183 | ) 184 | }) 185 | } 186 | } 187 | 188 | deserializer.deserialize_str(V) 189 | } 190 | } 191 | }; 192 | } 193 | 194 | /// Legacy alias for [`derive_deserialize_from_fromstr`]. 195 | #[deprecated(note = "legacy alias for derive_deserialize_from_fromstr")] 196 | #[doc(hidden)] 197 | #[macro_export] 198 | macro_rules! derive_deserialize_from_str { 199 | ($($tt:tt)*) => { $crate::derive_deserialize_from_fromstr!($($tt)*); } 200 | } 201 | 202 | /// Derives [`Serialize`](serde::Serialize) a type that implements [`Display`](std::fmt::Display). 203 | /// 204 | /// ```rust 205 | /// use std::fmt; 206 | /// use serde_plain::derive_serialize_from_display; 207 | /// # fn main() { 208 | /// 209 | /// pub struct MyStruct(u32); 210 | /// 211 | /// impl fmt::Display for MyStruct { 212 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 213 | /// write!(f, "{}", self.0) 214 | /// } 215 | /// } 216 | /// 217 | /// derive_serialize_from_display!(MyStruct); 218 | /// # } 219 | /// ``` 220 | /// 221 | /// This automatically implements [`Serialize`](serde::Serialize) which will 222 | /// invoke the [`to_string`](crate::to_string) method on the target. 223 | #[macro_export] 224 | macro_rules! derive_serialize_from_display { 225 | ($type:ident $(:: $type_extra:ident)* < $($lt:lifetime),+ >) => { 226 | impl<$($lt,)*> ::serde::ser::Serialize for $type$(:: $type_extra)*<$($lt,)*> { 227 | fn serialize(&self, serializer: S) -> ::std::result::Result 228 | where 229 | S: ::serde::ser::Serializer, 230 | { 231 | serializer.serialize_str(&self.to_string()) 232 | } 233 | } 234 | }; 235 | ($type:ty) => { 236 | impl ::serde::ser::Serialize for $type { 237 | fn serialize(&self, serializer: S) -> ::std::result::Result 238 | where 239 | S: ::serde::ser::Serializer, 240 | { 241 | serializer.serialize_str(&self.to_string()) 242 | } 243 | } 244 | }; 245 | } 246 | 247 | #[test] 248 | fn test_derive_display_from_serialize_lifetimes() { 249 | use serde_derive::Serialize; 250 | 251 | #[derive(Serialize)] 252 | struct MyType<'a>(&'a str); 253 | 254 | mod inner { 255 | use serde_derive::Serialize; 256 | 257 | #[derive(Serialize)] 258 | pub struct MyType<'a>(pub &'a str); 259 | } 260 | 261 | derive_display_from_serialize!(MyType<'a>); 262 | derive_display_from_serialize!(inner::MyType<'a>); 263 | 264 | assert_eq!(MyType("x").to_string(), "x"); 265 | assert_eq!(inner::MyType("x").to_string(), "x"); 266 | } 267 | 268 | #[test] 269 | fn test_derive_serialize_from_display_lifetimes() { 270 | use serde_derive::Deserialize; 271 | 272 | #[derive(Deserialize)] 273 | struct MyType<'a>(&'a str); 274 | 275 | impl<'a> std::fmt::Display for MyType<'a> { 276 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 277 | write!(f, "{}", self.0) 278 | } 279 | } 280 | 281 | mod inner { 282 | use serde_derive::Deserialize; 283 | 284 | #[derive(Deserialize)] 285 | pub struct MyType<'a>(pub &'a str); 286 | 287 | impl<'a> std::fmt::Display for MyType<'a> { 288 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 289 | write!(f, "{}", self.0) 290 | } 291 | } 292 | } 293 | 294 | derive_serialize_from_display!(MyType<'a>); 295 | derive_serialize_from_display!(inner::MyType<'a>); 296 | 297 | assert_eq!(crate::to_string(&MyType("x")).unwrap(), "x"); 298 | assert_eq!(crate::to_string(&inner::MyType("x")).unwrap(), "x"); 299 | } 300 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | use serde::ser; 2 | 3 | use crate::error::Error; 4 | 5 | /// A simple serializer that can dump out strings. 6 | pub struct Serializer; 7 | 8 | macro_rules! serialize_as_string { 9 | ($($ty:ty => $meth:ident,)*) => { 10 | $(fn $meth(self, v: $ty) -> Result { Ok(v.to_string()) })* 11 | }; 12 | } 13 | 14 | impl ser::Serializer for Serializer { 15 | type Ok = String; 16 | type Error = Error; 17 | type SerializeSeq = ser::Impossible; 18 | type SerializeTuple = ser::Impossible; 19 | type SerializeTupleStruct = ser::Impossible; 20 | type SerializeTupleVariant = ser::Impossible; 21 | type SerializeMap = ser::Impossible; 22 | type SerializeStruct = ser::Impossible; 23 | type SerializeStructVariant = ser::Impossible; 24 | 25 | serialize_as_string! { 26 | bool => serialize_bool, 27 | u8 => serialize_u8, 28 | u16 => serialize_u16, 29 | u32 => serialize_u32, 30 | u64 => serialize_u64, 31 | i8 => serialize_i8, 32 | i16 => serialize_i16, 33 | i32 => serialize_i32, 34 | i64 => serialize_i64, 35 | f32 => serialize_f32, 36 | f64 => serialize_f64, 37 | char => serialize_char, 38 | &str => serialize_str, 39 | } 40 | 41 | fn serialize_bytes(self, _value: &[u8]) -> Result { 42 | Err(Error::ImpossibleSerialization("bytes")) 43 | } 44 | 45 | fn serialize_unit(self) -> Result { 46 | Ok("".to_string()) 47 | } 48 | 49 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 50 | Err(Error::ImpossibleSerialization("unit struct")) 51 | } 52 | 53 | fn serialize_unit_variant( 54 | self, 55 | _name: &'static str, 56 | _variant_index: u32, 57 | variant: &'static str, 58 | ) -> Result { 59 | Ok(variant.to_string()) 60 | } 61 | 62 | fn serialize_newtype_struct( 63 | self, 64 | _name: &'static str, 65 | value: &T, 66 | ) -> Result { 67 | value.serialize(self) 68 | } 69 | 70 | fn serialize_newtype_variant( 71 | self, 72 | _name: &'static str, 73 | _variant_index: u32, 74 | _variant: &'static str, 75 | _value: &T, 76 | ) -> Result { 77 | Err(Error::ImpossibleSerialization("newtype variant")) 78 | } 79 | 80 | fn serialize_none(self) -> Result { 81 | Ok("".to_string()) 82 | } 83 | 84 | fn serialize_some(self, value: &T) -> Result { 85 | value.serialize(self) 86 | } 87 | 88 | fn serialize_seq(self, _len: Option) -> Result { 89 | Err(Error::ImpossibleSerialization("seq")) 90 | } 91 | 92 | fn serialize_tuple(self, _len: usize) -> Result { 93 | Err(Error::ImpossibleSerialization("tuple")) 94 | } 95 | 96 | fn serialize_tuple_struct( 97 | self, 98 | _name: &'static str, 99 | _len: usize, 100 | ) -> Result { 101 | Err(Error::ImpossibleSerialization("tuple struct")) 102 | } 103 | 104 | fn serialize_tuple_variant( 105 | self, 106 | _name: &'static str, 107 | _variant_index: u32, 108 | _variant: &'static str, 109 | _len: usize, 110 | ) -> Result { 111 | Err(Error::ImpossibleSerialization("tuple variant")) 112 | } 113 | 114 | fn serialize_map(self, _len: Option) -> Result { 115 | Err(Error::ImpossibleSerialization("map")) 116 | } 117 | 118 | fn serialize_struct( 119 | self, 120 | _name: &'static str, 121 | _len: usize, 122 | ) -> Result { 123 | Err(Error::ImpossibleSerialization("struct")) 124 | } 125 | 126 | fn serialize_struct_variant( 127 | self, 128 | _name: &'static str, 129 | _variant_index: u32, 130 | _variant: &'static str, 131 | _len: usize, 132 | ) -> Result { 133 | Err(Error::ImpossibleSerialization("struct variant")) 134 | } 135 | } 136 | 137 | /// Serialize the given data value as a plain string. 138 | /// 139 | /// This serializes an object with the `Serializer` into a string and then 140 | /// returns it. This requires that the type is a simple one (integer, string, 141 | /// etc.). 142 | pub fn to_string(value: &T) -> Result { 143 | value.serialize(Serializer) 144 | } 145 | -------------------------------------------------------------------------------- /tests/test_de.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | use serde_plain::derive_fromstr_from_deserialize; 5 | 6 | use std::str::FromStr; 7 | 8 | #[derive(Deserialize, PartialEq, Eq, Debug)] 9 | #[serde(rename_all = "snake_case")] 10 | pub enum Test { 11 | FooBarBaz, 12 | BlahBlah, 13 | } 14 | 15 | #[derive(Deserialize, PartialEq, Eq, Debug)] 16 | #[serde(rename_all = "snake_case")] 17 | pub enum FooType { 18 | FooA, 19 | FooB, 20 | } 21 | #[derive(Deserialize, PartialEq, Eq, Debug)] 22 | #[serde(rename_all = "snake_case")] 23 | pub enum BarType { 24 | BarA, 25 | BarB, 26 | } 27 | 28 | #[derive(Deserialize, PartialEq, Eq, Debug)] 29 | #[serde(untagged)] 30 | pub enum TestUntagged { 31 | Foo(FooType), 32 | Bar(BarType), 33 | Other(String), 34 | } 35 | 36 | derive_fromstr_from_deserialize!(TestUntagged); 37 | 38 | #[derive(Deserialize, PartialEq, Eq, Debug)] 39 | pub struct NewInt(pub i32); 40 | 41 | impl FromStr for Test { 42 | type Err = serde_plain::Error; 43 | fn from_str(value: &str) -> Result { 44 | serde_plain::from_str(value) 45 | } 46 | } 47 | 48 | #[test] 49 | fn test_basics() { 50 | assert_eq!(serde_plain::from_str::<&str>("aha").unwrap(), "aha"); 51 | assert_eq!(serde_plain::from_str::("42").unwrap(), 42); 52 | assert_eq!(serde_plain::from_str::("true").unwrap(), true); 53 | assert_eq!(serde_plain::from_str::("false").unwrap(), false); 54 | assert_eq!(serde_plain::from_str::<()>("").unwrap(), ()); 55 | assert_eq!(serde_plain::from_str::>("").unwrap(), None); 56 | assert_eq!( 57 | serde_plain::from_str::>("42").unwrap(), 58 | Some("42".into()) 59 | ); 60 | assert_eq!( 61 | serde_plain::from_str::>("42").unwrap(), 62 | Some("42") 63 | ); 64 | assert_eq!( 65 | serde_plain::from_str::("blah_blah").unwrap(), 66 | Test::BlahBlah 67 | ); 68 | assert_eq!(serde_plain::from_str::("42").unwrap(), NewInt(42)); 69 | serde_plain::from_str::("blah_blah") 70 | .expect("doesn't fail on #[serde(skip)] fields"); 71 | } 72 | 73 | #[test] 74 | fn test_untagged_enum() { 75 | let pairs = [ 76 | ("foo_a", TestUntagged::Foo(FooType::FooA)), 77 | ("foo_b", TestUntagged::Foo(FooType::FooB)), 78 | ("bar_a", TestUntagged::Bar(BarType::BarA)), 79 | ("bar_b", TestUntagged::Bar(BarType::BarB)), 80 | ("other", TestUntagged::Other("other".to_string())), 81 | ]; 82 | 83 | for (string, expected) in pairs.iter() { 84 | assert_eq!(string.parse::().unwrap(), *expected); 85 | } 86 | } 87 | 88 | #[test] 89 | fn test_from_str() { 90 | assert_eq!("foo_bar_baz".parse::().unwrap(), Test::FooBarBaz); 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_macros.rs: -------------------------------------------------------------------------------- 1 | use serde_plain::{ 2 | derive_deserialize_from_fromstr, derive_display_from_serialize, 3 | derive_fromstr_from_deserialize, derive_serialize_from_display, 4 | }; 5 | use std::{fmt, num, str}; 6 | 7 | #[macro_use] 8 | extern crate serde_derive; 9 | 10 | #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] 11 | #[serde(rename_all = "snake_case")] 12 | pub enum Test { 13 | FooBarBaz, 14 | BlahBlah, 15 | } 16 | 17 | #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] 18 | pub enum Test2 { 19 | FooBarBaz, 20 | BlahBlah, 21 | } 22 | 23 | #[derive(Debug, PartialEq, Eq)] 24 | pub struct Test2Error; 25 | 26 | impl From for Test2Error { 27 | fn from(_: serde_plain::Error) -> Test2Error { 28 | Test2Error 29 | } 30 | } 31 | 32 | #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] 33 | pub enum Test3 { 34 | FooBarBaz, 35 | BlahBlah, 36 | } 37 | 38 | #[derive(Debug, PartialEq, Eq)] 39 | pub struct Test3Error(String); 40 | 41 | pub struct TestStruct(u32); 42 | 43 | impl str::FromStr for TestStruct { 44 | type Err = num::ParseIntError; 45 | fn from_str(value: &str) -> Result { 46 | Ok(TestStruct(value.parse()?)) 47 | } 48 | } 49 | 50 | impl fmt::Display for TestStruct { 51 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 52 | write!(f, "{}", self.0) 53 | } 54 | } 55 | 56 | derive_fromstr_from_deserialize!(Test); 57 | derive_display_from_serialize!(Test); 58 | 59 | derive_fromstr_from_deserialize!(Test2, Test2Error); 60 | derive_display_from_serialize!(Test2); 61 | 62 | derive_fromstr_from_deserialize!(Test3, |err| -> Test3Error { Test3Error(err.to_string()) }); 63 | derive_display_from_serialize!(Test3); 64 | 65 | derive_deserialize_from_fromstr!(TestStruct, "valid positive number"); 66 | derive_serialize_from_display!(TestStruct); 67 | 68 | #[test] 69 | fn test_forward_basics() { 70 | assert_eq!(Test::FooBarBaz.to_string(), "foo_bar_baz"); 71 | assert_eq!("foo_bar_baz".parse::().unwrap(), Test::FooBarBaz); 72 | } 73 | 74 | #[test] 75 | fn test_forward_custom_error() { 76 | assert_eq!("whatever".parse::(), Err(Test2Error)); 77 | } 78 | 79 | #[test] 80 | fn test_forward_custom_error_conversion() { 81 | assert_eq!( 82 | "whatever".parse::(), 83 | Err(Test3Error( 84 | "unknown variant `whatever`, expected `FooBarBaz` or `BlahBlah`".to_string() 85 | )) 86 | ); 87 | } 88 | 89 | #[test] 90 | fn test_derive_deserialize() { 91 | let test: TestStruct = serde_plain::from_str("42").unwrap(); 92 | assert_eq!(TestStruct(42).0, test.0); 93 | } 94 | 95 | #[test] 96 | fn test_derive_serialize() { 97 | let test = serde_plain::to_string(&TestStruct(42)).unwrap(); 98 | assert_eq!("42", test.as_str()); 99 | } 100 | -------------------------------------------------------------------------------- /tests/test_ser.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | use std::fmt; 5 | 6 | #[derive(Serialize)] 7 | #[serde(rename_all = "snake_case")] 8 | pub enum Test { 9 | FooBarBaz, 10 | BlahBlah, 11 | } 12 | 13 | #[derive(Serialize)] 14 | pub struct NewInt(i32); 15 | 16 | impl fmt::Display for Test { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | write!(f, "{}", serde_plain::to_string(self).unwrap()) 19 | } 20 | } 21 | 22 | impl ToString for NewInt { 23 | fn to_string(&self) -> String { 24 | serde_plain::to_string(self).unwrap() 25 | } 26 | } 27 | 28 | #[test] 29 | fn test_basics() { 30 | assert_eq!(serde_plain::to_string(&42).unwrap(), "42"); 31 | assert_eq!(serde_plain::to_string(&"blafasel").unwrap(), "blafasel"); 32 | assert_eq!( 33 | serde_plain::to_string(&Test::FooBarBaz).unwrap(), 34 | "foo_bar_baz" 35 | ); 36 | assert_eq!( 37 | serde_plain::to_string(&Test::BlahBlah).unwrap(), 38 | "blah_blah" 39 | ); 40 | assert_eq!(serde_plain::to_string(&NewInt(42)).unwrap(), "42"); 41 | assert_eq!(serde_plain::to_string(&Some(true)).unwrap(), "true"); 42 | assert_eq!(serde_plain::to_string(&None::<()>).unwrap(), ""); 43 | assert_eq!(serde_plain::to_string(&()).unwrap(), ""); 44 | } 45 | 46 | #[test] 47 | fn test_to_string() { 48 | assert_eq!(Test::FooBarBaz.to_string(), "foo_bar_baz"); 49 | assert_eq!(NewInt(42).to_string(), "42"); 50 | } 51 | --------------------------------------------------------------------------------