@@ -56,3 +56,120 @@ fn test_to_ascii_lowercase() {
5656 Cow :: Owned ( expected) if expected == "hello, 🌎!"
5757 ) ) ;
5858}
59+
60+ /// Contains serde functions for types that are sent and received as strings but aren't surfaced as strings.
61+ pub mod as_string {
62+ use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
63+
64+ /// Deserializes a string into a T. T must implement [`std::str::FromStr`].
65+ pub fn deserialize < ' de , D , T > ( deserializer : D ) -> Result < Option < T > , D :: Error >
66+ where
67+ D : Deserializer < ' de > ,
68+ T : std:: str:: FromStr ,
69+ <T as std:: str:: FromStr >:: Err : std:: fmt:: Display ,
70+ {
71+ let to_deserialize = <Option < String > >:: deserialize ( deserializer) ?;
72+ match to_deserialize {
73+ Some ( to_deserialize) => Ok ( Some (
74+ T :: from_str ( & to_deserialize) . map_err ( serde:: de:: Error :: custom) ?,
75+ ) ) ,
76+ None => Ok ( None ) ,
77+ }
78+ }
79+
80+ /// Serializes T in string format. T must implement [`std::string::ToString`].
81+ pub fn serialize < S , T > ( to_serialize : & Option < T > , serializer : S ) -> Result < S :: Ok , S :: Error >
82+ where
83+ S : Serializer ,
84+ T : std:: string:: ToString ,
85+ {
86+ match to_serialize {
87+ Some ( to_serialize) => String :: serialize ( & to_serialize. to_string ( ) , serializer) ,
88+ None => serializer. serialize_none ( ) ,
89+ }
90+ }
91+ }
92+
93+ #[ cfg( test) ]
94+ mod tests {
95+ use crate :: json:: { from_json, to_json} ;
96+ use serde:: { Deserialize , Serialize } ;
97+ use serde_json:: Number ;
98+
99+ #[ derive( Default , Deserialize , Serialize ) ]
100+ struct TestType {
101+ #[ serde(
102+ default ,
103+ skip_serializing_if = "Option::is_none" ,
104+ with = "super::as_string"
105+ ) ]
106+ a_bool : Option < bool > ,
107+
108+ #[ serde(
109+ default ,
110+ skip_serializing_if = "Option::is_none" ,
111+ with = "super::as_string"
112+ ) ]
113+ json_number : Option < serde_json:: Number > ,
114+
115+ #[ serde(
116+ default ,
117+ skip_serializing_if = "Option::is_none" ,
118+ with = "super::as_string"
119+ ) ]
120+ some_float : Option < f64 > ,
121+
122+ #[ serde(
123+ default ,
124+ skip_serializing_if = "Option::is_none" ,
125+ with = "super::as_string"
126+ ) ]
127+ some_int : Option < i32 > ,
128+ }
129+
130+ #[ test]
131+ fn test_deserialize_none ( ) -> crate :: Result < ( ) > {
132+ let json_body = r#"{}"# ;
133+ let test_type: TestType = from_json ( json_body) ?;
134+ assert ! ( test_type. a_bool. is_none( ) ) ;
135+ assert ! ( test_type. json_number. is_none( ) ) ;
136+ assert ! ( test_type. some_float. is_none( ) ) ;
137+ assert ! ( test_type. some_int. is_none( ) ) ;
138+ Ok ( ( ) )
139+ }
140+
141+ #[ test]
142+ fn test_deserialize_all ( ) -> crate :: Result < ( ) > {
143+ let json_body = r#"{"a_bool":"true","json_number":"123456789","some_float":"9.87654321","some_int":"42"}"# ;
144+ let test_type: TestType = from_json ( json_body) ?;
145+ assert_eq ! ( test_type. a_bool, Some ( true ) ) ;
146+ assert_eq ! ( test_type. json_number, Number :: from_i128( 123456789 ) ) ;
147+ assert_eq ! ( test_type. some_float, Some ( 9.87654321 ) ) ;
148+ assert_eq ! ( test_type. some_int, Some ( 42 ) ) ;
149+ Ok ( ( ) )
150+ }
151+
152+ #[ test]
153+ fn test_serialize_none ( ) -> crate :: Result < ( ) > {
154+ let test_type = TestType :: default ( ) ;
155+ let json_body = to_json ( & test_type) ?;
156+ assert_eq ! ( json_body, r#"{}"# ) ;
157+ Ok ( ( ) )
158+ }
159+
160+ #[ test]
161+ fn test_serialize_all ( ) -> crate :: Result < ( ) > {
162+ let test_type = TestType {
163+ a_bool : Some ( true ) ,
164+ json_number : Number :: from_i128 ( 123456789 ) ,
165+ some_float : Some ( 9.87654321 ) ,
166+ some_int : Some ( 42 ) ,
167+ } ;
168+ let json_body = to_json ( & test_type) ?;
169+ assert_eq ! (
170+ json_body,
171+ r#"{"a_bool":"true","json_number":"123456789","some_float":"9.87654321","some_int":"42"}"#
172+ ) ;
173+ Ok ( ( ) )
174+ }
175+ }
0 commit comments