1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// Licensed under the MIT License.
3
3
4
- /// The following macro invocation:
4
+ /// Creates an enum with a fixed set of variants.
5
+ ///
6
+ /// This macro creates an enum where each variant can be turned into and constructed from the corresponding string.
7
+ /// The [`std::str::FromStr`] implementation will return a [`typespec::error::Error`] if not supported (case-sensitive).
8
+ ///
9
+ /// # Examples
10
+ ///
5
11
/// ```
6
12
/// # #[macro_use] extern crate typespec_client_core;
7
- /// create_enum!(Words, (Chicken, "Chicken"), (White, "White"), (Yellow, "Yellow"));
13
+ /// create_enum!(
14
+ /// #[doc = "Example words"]
15
+ /// Words,
16
+ /// #[doc = "Poultry"]
17
+ /// (Chicken, "Chicken"),
18
+ /// (White, "White"),
19
+ /// (Yellow, "Yellow")
20
+ /// );
21
+ ///
22
+ /// let word = Words::Chicken;
23
+ /// assert_eq!(word.to_string(), String::from("Chicken"));
8
24
/// ```
9
- /// Turns into a struct where each variant can be turned into and construct from the corresponding string.
10
25
#[ macro_export]
11
26
macro_rules! create_enum {
12
- ( $( #[ $type_doc : meta] ) * $name: ident, $( $( #[ $val_doc : meta] ) * ( $variant: ident, $value: expr) ) , * ) => (
13
- $( #[ $type_doc ] ) *
27
+ ( $( #[ $type_meta : meta] ) * $name: ident, $( $( #[ $value_meta : meta] ) * ( $variant: ident, $value: expr) ) , * ) => (
28
+ $( #[ $type_meta ] ) *
14
29
#[ derive( Debug , PartialEq , Eq , PartialOrd , Clone , Copy ) ]
15
30
pub enum $name {
16
31
$(
17
- $( #[ $val_doc ] ) *
32
+ $( #[ $value_meta ] ) *
18
33
$variant,
19
34
) *
20
35
}
@@ -29,12 +44,6 @@ macro_rules! create_enum {
29
44
}
30
45
}
31
46
32
- impl $crate:: parsing:: FromStringOptional <$name> for $name {
33
- fn from_str_optional( s : & str ) -> $crate:: error:: Result <$name> {
34
- s. parse:: <$name>( )
35
- }
36
- }
37
-
38
47
impl :: std:: str :: FromStr for $name {
39
48
type Err = $crate:: error:: Error ;
40
49
@@ -51,19 +60,37 @@ macro_rules! create_enum {
51
60
}
52
61
}
53
62
63
+ impl :: std:: convert:: AsRef <str > for $name {
64
+ fn as_ref( & self ) -> & str {
65
+ match self {
66
+ $(
67
+ $name:: $variant => $value,
68
+ ) *
69
+ }
70
+ }
71
+ }
72
+
73
+ impl :: std:: fmt:: Display for $name {
74
+ fn fmt( & self , f: & mut :: std:: fmt:: Formatter <' _>) -> :: std:: fmt:: Result {
75
+ match self {
76
+ $(
77
+ $name:: $variant => write!( f, "{}" , $value) ,
78
+ ) *
79
+ }
80
+ }
81
+ }
82
+
83
+ create_enum!( @intern $name) ;
84
+ ) ;
85
+
86
+ ( @intern $name: ident) => (
54
87
impl <' de> serde:: Deserialize <' de> for $name {
55
88
fn deserialize<D >( deserializer: D ) -> :: core:: result:: Result <Self , D :: Error >
56
89
where
57
90
D : serde:: Deserializer <' de>,
58
91
{
59
92
let s = String :: deserialize( deserializer) ?;
60
-
61
- match s. as_ref( ) {
62
- $(
63
- $value => Ok ( Self :: $variant) ,
64
- ) *
65
- _ => Err ( serde:: de:: Error :: custom( "unsupported value" ) ) ,
66
- }
93
+ s. parse( ) . map_err( serde:: de:: Error :: custom)
67
94
}
68
95
}
69
96
@@ -74,26 +101,97 @@ macro_rules! create_enum {
74
101
}
75
102
}
76
103
104
+ impl $crate:: parsing:: FromStringOptional <$name> for $name {
105
+ fn from_str_optional( s : & str ) -> $crate:: error:: Result <$name> {
106
+ s. parse:: <$name>( ) . map_err( :: core:: convert:: Into :: into)
107
+ }
108
+ }
109
+ ) ;
110
+ }
111
+
112
+ /// Creates an enum with a set of variants including `UnknownValue` which holds any unsupported string from which it was created.
113
+ ///
114
+ /// This macro creates an enum where each variant can be turned into and constructed from the corresponding string.
115
+ /// The [`std::str::FromStr`] implementation will not return an error but instead store the string in `UnknownValue(String)`.
116
+ ///
117
+ /// # Examples
118
+ ///
119
+ /// ```
120
+ /// # #[macro_use] extern crate typespec_client_core;
121
+ /// create_extensible_enum!(
122
+ /// #[doc = "Example words"]
123
+ /// Words,
124
+ /// #[doc = "Poultry"]
125
+ /// (Chicken, "Chicken"),
126
+ /// (White, "White"),
127
+ /// (Yellow, "Yellow")
128
+ /// );
129
+ ///
130
+ /// let word: Words = "Turkey".parse().unwrap();
131
+ /// assert_eq!(word.to_string(), String::from("Turkey"));
132
+ /// ```
133
+ #[ macro_export]
134
+ macro_rules! create_extensible_enum {
135
+ ( $( #[ $type_meta: meta] ) * $name: ident, $( $( #[ $value_meta: meta] ) * ( $variant: ident, $value: expr) ) , * ) => (
136
+ $( #[ $type_meta] ) *
137
+ #[ derive( Debug , PartialEq , Eq , PartialOrd , Clone ) ]
138
+ pub enum $name {
139
+ $(
140
+ $( #[ $value_meta] ) *
141
+ $variant,
142
+ ) *
143
+ /// Any other value not defined in `$name`.
144
+ UnknownValue ( String ) ,
145
+ }
146
+
147
+ impl <' a> :: std:: convert:: From <& ' a $name> for & ' a str {
148
+ fn from( e: & ' a $name) -> Self {
149
+ match e {
150
+ $(
151
+ $name:: $variant => $value,
152
+ ) *
153
+ $name:: UnknownValue ( s) => s. as_ref( ) ,
154
+ }
155
+ }
156
+ }
157
+
158
+ impl :: std:: str :: FromStr for $name {
159
+ type Err = :: std:: convert:: Infallible ;
160
+
161
+ fn from_str( s: & str ) -> :: core:: result:: Result <Self , <Self as :: std:: str :: FromStr >:: Err > {
162
+ Ok ( match s {
163
+ $(
164
+ $value => $name:: $variant,
165
+ ) *
166
+ _ => $name:: UnknownValue ( s. to_string( ) ) ,
167
+ } )
168
+ }
169
+ }
170
+
77
171
impl :: std:: convert:: AsRef <str > for $name {
78
172
fn as_ref( & self ) -> & str {
79
- match * self {
173
+ match self {
80
174
$(
81
175
$name:: $variant => $value,
82
176
) *
177
+ $name:: UnknownValue ( s) => s. as_str( ) ,
83
178
}
84
179
}
85
180
}
86
181
87
182
impl :: std:: fmt:: Display for $name {
88
183
fn fmt( & self , f: & mut :: std:: fmt:: Formatter <' _>) -> :: std:: fmt:: Result {
89
- match * self {
184
+ match self {
90
185
$(
91
- $name:: $variant => write! ( f , "{}" , $value) ,
186
+ $name:: $variant => f . write_str ( $value) ,
92
187
) *
188
+ $name:: UnknownValue ( s) => f. write_str( s. as_str( ) ) ,
93
189
}
94
190
}
95
191
}
96
- )
192
+
193
+ create_enum!( @intern $name) ;
194
+ ) ;
97
195
}
98
196
99
197
/// Creates setter methods
@@ -155,19 +253,22 @@ macro_rules! setters {
155
253
156
254
#[ cfg( test) ]
157
255
mod test {
256
+ use serde:: { Deserialize , Serialize } ;
257
+
158
258
create_enum ! ( Colors , ( Black , "Black" ) , ( White , "White" ) , ( Red , "Red" ) ) ;
159
259
create_enum ! ( ColorsMonochrome , ( Black , "Black" ) , ( White , "White" ) ) ;
160
260
161
- create_enum ! (
162
- #[ doc = "Defines operation states" ]
163
- OperationState ,
164
- #[ doc = "The operation hasn't started" ]
165
- ( NotStarted , "notStarted" ) ,
166
- #[ doc = "The operation is in progress" ]
167
- ( InProgress , "inProgress" ) ,
168
- #[ doc = "The operation has completed" ]
169
- ( Completed , "completed" )
170
- ) ;
261
+ // cspell:ignore metasyntactic
262
+ create_extensible_enum ! ( Metasyntactic , ( Foo , "foo" ) , ( Bar , "bar" ) ) ;
263
+
264
+ #[ derive( Debug , Default , Deserialize , Serialize ) ]
265
+ #[ serde( default ) ]
266
+ struct TestData {
267
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
268
+ color : Option < Colors > ,
269
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
270
+ meta : Option < Metasyntactic > ,
271
+ }
171
272
172
273
struct Options {
173
274
a : Option < String > ,
@@ -189,27 +290,63 @@ mod test {
189
290
}
190
291
191
292
#[ test]
192
- fn test_color_parse_1 ( ) {
293
+ fn color_parse_1 ( ) {
193
294
let color = "Black" . parse :: < Colors > ( ) . unwrap ( ) ;
194
295
assert_eq ! ( Colors :: Black , color) ;
195
296
}
196
297
197
298
#[ test]
198
- fn test_color_parse_2 ( ) {
299
+ fn color_parse_2 ( ) {
199
300
let color = "White" . parse :: < ColorsMonochrome > ( ) . unwrap ( ) ;
200
301
assert_eq ! ( ColorsMonochrome :: White , color) ;
201
302
}
202
303
203
304
#[ test]
204
- fn test_color_parse_err_1 ( ) {
305
+ fn color_parse_err_1 ( ) {
205
306
"Red" . parse :: < ColorsMonochrome > ( ) . unwrap_err ( ) ;
206
307
}
207
308
208
309
#[ test]
209
- fn test_setters ( ) {
310
+ fn setters ( ) {
210
311
let options = Options :: default ( ) . a ( "test" . to_owned ( ) ) ;
211
312
212
313
assert_eq ! ( Some ( "test" . to_owned( ) ) , options. a) ;
213
314
assert_eq ! ( 1 , options. b) ;
214
315
}
316
+
317
+ #[ test]
318
+ fn deserialize_enum ( ) {
319
+ let data: TestData = serde_json:: from_str ( r#"{"color": "Black"}"# ) . unwrap ( ) ;
320
+ assert_eq ! ( Some ( Colors :: Black ) , data. color) ;
321
+ }
322
+
323
+ #[ test]
324
+ fn deserialize_extensible_enum ( ) {
325
+ // Variant values are case-sensitive.
326
+ let data: TestData = serde_json:: from_str ( r#"{"meta": "Foo"}"# ) . unwrap ( ) ;
327
+ assert_eq ! (
328
+ Some ( Metasyntactic :: UnknownValue ( String :: from( "Foo" ) ) ) ,
329
+ data. meta
330
+ ) ;
331
+ }
332
+
333
+ #[ test]
334
+ fn serialize_enum ( ) {
335
+ let data = TestData {
336
+ color : Some ( Colors :: Red ) ,
337
+ ..Default :: default ( )
338
+ } ;
339
+ let json = serde_json:: to_string ( & data) . unwrap ( ) ;
340
+ assert_eq ! ( String :: from( r#"{"color":"Red"}"# ) , json) ;
341
+ }
342
+
343
+ #[ test]
344
+ fn serialize_extensible_enum ( ) {
345
+ let data = TestData {
346
+ meta : Some ( Metasyntactic :: Foo ) ,
347
+ ..Default :: default ( )
348
+ } ;
349
+ let json = serde_json:: to_string ( & data) . unwrap ( ) ;
350
+ assert_eq ! ( String :: from( r#"{"meta":"foo"}"# ) , json) ;
351
+ }
215
352
}
0 commit comments