4
4
use crate :: DscError ;
5
5
use crate :: configure:: context:: Context ;
6
6
use crate :: functions:: { AcceptedArgKind , Function } ;
7
- use rt_format:: { Format as RtFormat , FormatArgument , ParsedFormat , Specifier } ;
7
+ use rt_format:: { Format as RtFormat , FormatArgument , ParsedFormat , argument :: NoNamedArguments } ;
8
8
use rust_i18n:: t;
9
9
use serde_json:: Value ;
10
- use std:: fmt;
11
- use std:: fmt:: { Error , Formatter , Write } ;
12
10
13
- impl FormatArgument for Value {
14
- fn supports_format ( & self , spec : & Specifier ) -> bool {
11
+ #[ derive( Debug , PartialEq ) ]
12
+ enum Variant {
13
+ Boolean ( bool ) ,
14
+ Number ( i64 ) ,
15
+ String ( String ) ,
16
+ }
17
+
18
+ impl FormatArgument for Variant {
19
+ fn supports_format ( & self , specifier : & rt_format:: Specifier ) -> bool {
20
+ match self {
21
+ Variant :: Boolean ( _) | Variant :: String ( _) => matches ! ( specifier. format, RtFormat :: Display ) ,
22
+ Variant :: Number ( _) => matches ! ( specifier. format, RtFormat :: Display | RtFormat :: Binary | RtFormat :: Octal | RtFormat :: LowerHex | RtFormat :: UpperHex | RtFormat :: Debug | RtFormat :: LowerExp | RtFormat :: UpperExp ) ,
23
+ }
24
+ }
25
+
26
+ fn fmt_display ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
15
27
match self {
16
- Value :: Boolean ( _) | Value :: String ( _) | Value :: Number ( _) => true ,
17
- _ => false ,
28
+ Variant :: Boolean ( value) => write ! ( f, "{value}" ) ,
29
+ Variant :: Number ( value) => write ! ( f, "{value}" ) ,
30
+ Variant :: String ( value) => write ! ( f, "{value}" ) ,
18
31
}
19
32
}
20
33
21
- fn fmt_display ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
34
+ fn fmt_octal ( & self , f : & mut std :: fmt :: Formatter ) -> std:: fmt:: Result {
22
35
match self {
23
- Value :: Boolean ( b) => write ! ( f, "{}" , b) ,
24
- Value :: String ( s) => write ! ( f, "{}" , s) ,
25
- Value :: Number ( n) => write ! ( f, "{}" , n) ,
26
- _ => Err ( fmt:: Error ) ,
36
+ Variant :: Number ( value) => write ! ( f, "{value:o}" ) ,
37
+ _ => Err ( std:: fmt:: Error ) ,
27
38
}
28
39
}
29
40
30
- fn fmt_lower_hex ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
41
+ fn fmt_lower_hex ( & self , f : & mut std :: fmt :: Formatter ) -> std:: fmt:: Result {
31
42
match self {
32
- Value :: Number ( n ) => write ! ( f, "{:x}" , n . as_i64 ( ) . unwrap_or_default ( ) ) ,
33
- _ => Err ( fmt:: Error ) ,
43
+ Variant :: Number ( value ) => write ! ( f, "{value :x}" ) ,
44
+ _ => Err ( std :: fmt:: Error ) ,
34
45
}
35
46
}
36
47
37
- fn fmt_upper_hex ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
48
+ fn fmt_upper_hex ( & self , f : & mut std :: fmt :: Formatter ) -> std:: fmt:: Result {
38
49
match self {
39
- Value :: Number ( n ) => write ! ( f, "{:X}" , n . as_i64 ( ) . unwrap_or_default ( ) ) ,
40
- _ => Err ( fmt:: Error ) ,
50
+ Variant :: Number ( value ) => write ! ( f, "{value :X}" ) ,
51
+ _ => Err ( std :: fmt:: Error ) ,
41
52
}
42
53
}
43
54
44
- fn fmt_binary ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
55
+ fn fmt_binary ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
56
+ match self {
57
+ Variant :: Number ( value) => write ! ( f, "{value:b}" ) ,
58
+ _ => Err ( std:: fmt:: Error ) ,
59
+ }
60
+ }
61
+
62
+ fn fmt_debug ( & self , _f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
63
+ Err ( std:: fmt:: Error )
64
+ }
65
+
66
+ fn fmt_lower_exp ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
45
67
match self {
46
- Value :: Number ( n ) => write ! ( f, "{:b}" , n . as_i64 ( ) . unwrap_or_default ( ) ) ,
47
- _ => Err ( fmt:: Error ) ,
68
+ Variant :: Number ( value ) => write ! ( f, "{value:e}" ) ,
69
+ _ => Err ( std :: fmt:: Error ) ,
48
70
}
49
71
}
50
72
51
- fn fmt_octal ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
73
+ fn fmt_upper_exp ( & self , f : & mut std :: fmt :: Formatter ) -> std:: fmt:: Result {
52
74
match self {
53
- Value :: Number ( n ) => write ! ( f, "{:o}" , n . as_i64 ( ) . unwrap_or_default ( ) ) ,
54
- _ => Err ( fmt:: Error ) ,
75
+ Variant :: Number ( value ) => write ! ( f, "{value:E}" ) ,
76
+ _ => Err ( std :: fmt:: Error ) ,
55
77
}
56
78
}
57
79
}
@@ -69,29 +91,33 @@ impl Function for Format {
69
91
}
70
92
71
93
fn accepted_arg_types ( & self ) -> Vec < AcceptedArgKind > {
72
- vec ! [ AcceptedArgKind :: Boolean , AcceptedArgKind :: String , AcceptedArgKind :: Number ]
94
+ vec ! [ AcceptedArgKind :: Boolean , AcceptedArgKind :: Number , AcceptedArgKind :: String ]
73
95
}
74
96
75
97
fn invoke ( & self , args : & [ Value ] , _context : & Context ) -> Result < Value , DscError > {
76
- let mut string_result = String :: new ( ) ;
77
- let Ok ( format_string) = args[ 0 ] . as_str ( ) else {
78
- return Err ( DscError :: Parser ( "First `format()` argument must be a string" . to_string ( ) ) ) ;
98
+ let Some ( format_string) = args[ 0 ] . as_str ( ) else {
99
+ return Err ( DscError :: Parser ( t ! ( "functions.format.formatInvalid" ) . to_string ( ) ) ) ;
79
100
} ;
101
+ let mut position_args = Vec :: new ( ) ;
80
102
for value in & args[ 1 ..] {
81
- if let Some ( parsed_format ) = ParsedFormat :: parse ( format_string ) {
82
- let mut formatted_string = String :: new ( ) ;
83
- for specifier in parsed_format . specifiers ( ) {
84
- if let Some ( arg ) = args . get ( specifier . index ( ) ) {
85
- formatted_string . push_str ( & arg . to_string ( ) ) ;
103
+ let arg = match value {
104
+ Value :: Bool ( b ) => Variant :: Boolean ( * b ) ,
105
+ Value :: Number ( n ) => {
106
+ if let Some ( i ) = n . as_i64 ( ) {
107
+ Variant :: Number ( i )
86
108
} else {
87
- return Err ( DscError :: Parser ( "Invalid format specifier" . to_string ( ) ) ) ;
109
+ return Err ( DscError :: Parser ( t ! ( "functions. format.numberTooLarge" ) . to_string ( ) ) ) ;
88
110
}
89
111
}
90
- string_result . push_str ( & formatted_string ) ;
91
- } else {
92
- return Err ( DscError :: Parser ( "Invalid format string" . to_string ( ) ) ) ;
93
- }
112
+ Value :: String ( s ) => Variant :: String ( s . clone ( ) ) ,
113
+ _ => return Err ( DscError :: Parser ( t ! ( "functions.format.invalidArgType" ) . to_string ( ) ) ) ,
114
+ } ;
115
+ position_args . push ( arg ) ;
94
116
}
117
+ let string_result = match ParsedFormat :: parse ( format_string, & position_args, & NoNamedArguments ) {
118
+ Ok ( parsed_format) => format ! ( "{parsed_format}" ) ,
119
+ Err ( _e) => return Err ( DscError :: Parser ( t ! ( "functions.format.invalidFormatString" ) . to_string ( ) ) ) ,
120
+ } ;
95
121
Ok ( Value :: String ( string_result) )
96
122
}
97
123
}
@@ -104,15 +130,154 @@ mod tests {
104
130
#[ test]
105
131
fn position ( ) {
106
132
let mut parser = Statement :: new ( ) . unwrap ( ) ;
107
- let result = parser. parse_and_execute ( "[format('{0} - {1}', 'hello', 2)]" , & Context :: new ( ) ) . unwrap ( ) ;
108
- assert_eq ! ( result, "hello - 2" ) ;
133
+ let result = parser. parse_and_execute ( "[format('world {0} - {1}', 'hello', 2)]" , & Context :: new ( ) ) . unwrap ( ) ;
134
+ assert_eq ! ( result, "world hello - 2" ) ;
135
+ }
136
+
137
+ #[ test]
138
+ fn reverse_position ( ) {
139
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
140
+ let result = parser. parse_and_execute ( "[format('two{1} - {0}world', 'hello', 2)]" , & Context :: new ( ) ) . unwrap ( ) ;
141
+ assert_eq ! ( result, "two2 - helloworld" ) ;
142
+ }
143
+
144
+ #[ test]
145
+ fn repeated_position ( ) {
146
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
147
+ let result = parser. parse_and_execute ( "[format('{0} - {0}{1}', 'hello', 2)]" , & Context :: new ( ) ) . unwrap ( ) ;
148
+ assert_eq ! ( result, "hello - hello2" ) ;
109
149
}
110
150
111
151
#[ test]
112
152
fn numbers_as_hex ( ) {
113
153
let mut parser = Statement :: new ( ) . unwrap ( ) ;
114
- let result = parser. parse_and_execute ( "[format('{0:x} {0:X}', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
115
- assert_eq ! ( result, "c D" ) ;
154
+ let result = parser. parse_and_execute ( "[format('{0:x} = {1:X}', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
155
+ assert_eq ! ( result, "c = D" ) ;
156
+ }
157
+
158
+ #[ test]
159
+ fn numbers_as_octal ( ) {
160
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
161
+ let result = parser. parse_and_execute ( "[format('{0:o} == {1:o}', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
162
+ assert_eq ! ( result, "14 == 15" ) ;
163
+ }
164
+
165
+ #[ test]
166
+ fn numbers_as_binary ( ) {
167
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
168
+ let result = parser. parse_and_execute ( "[format('{0:b} = {1:b}', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
169
+ assert_eq ! ( result, "1100 = 1101" ) ;
170
+ }
171
+
172
+ #[ test]
173
+ fn numbers_as_exp ( ) {
174
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
175
+ let result = parser. parse_and_execute ( "[format('{0:e} = {1:E}', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
176
+ assert_eq ! ( result, "1.2e1 = 1.3E1" ) ;
177
+ }
178
+
179
+ #[ test]
180
+ fn numbers_as_display_just_one ( ) {
181
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
182
+ let result = parser. parse_and_execute ( "[format('hello {0} there', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
183
+ assert_eq ! ( result, "hello 12 there" ) ;
184
+ }
185
+
186
+ #[ test]
187
+ fn string_as_octal ( ) {
188
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
189
+ let result = parser. parse_and_execute ( "[format('{0:o} = {1:O}', 'hello', 13)]" , & Context :: new ( ) ) ;
190
+ assert ! ( result. is_err( ) ) ;
191
+ }
192
+
193
+ #[ test]
194
+ fn bool_as_octal ( ) {
195
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
196
+ let result = parser. parse_and_execute ( "[format('{0:o} = {1:O}', true, 13)]" , & Context :: new ( ) ) ;
197
+ assert ! ( result. is_err( ) ) ;
198
+ }
199
+
200
+ #[ test]
201
+ fn string_as_hex ( ) {
202
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
203
+ let result = parser. parse_and_execute ( "[format('{0:x} = {1:X}', 'hello', 13)]" , & Context :: new ( ) ) ;
204
+ assert ! ( result. is_err( ) ) ;
205
+ }
206
+
207
+ #[ test]
208
+ fn bool_as_hex ( ) {
209
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
210
+ let result = parser. parse_and_execute ( "[format('{0:x} = {1:X}', true, 13)]" , & Context :: new ( ) ) ;
211
+ assert ! ( result. is_err( ) ) ;
212
+ }
213
+
214
+ #[ test]
215
+ fn string_as_binary ( ) {
216
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
217
+ let result = parser. parse_and_execute ( "[format('{1:b} = {0:B}', 'hello', 13)]" , & Context :: new ( ) ) ;
218
+ assert ! ( result. is_err( ) ) ;
219
+ }
220
+
221
+ #[ test]
222
+ fn bool_as_binary ( ) {
223
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
224
+ let result = parser. parse_and_execute ( "[format('{1:b} = {0:B}', true, 13)]" , & Context :: new ( ) ) ;
225
+ assert ! ( result. is_err( ) ) ;
226
+ }
227
+
228
+ #[ test]
229
+ fn string_as_exp ( ) {
230
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
231
+ let result = parser. parse_and_execute ( "[format('{1:e} = {0:E}', 'hello', 13)]" , & Context :: new ( ) ) ;
232
+ assert ! ( result. is_err( ) ) ;
233
+ }
234
+
235
+ #[ test]
236
+ fn bool_as_exp ( ) {
237
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
238
+ let result = parser. parse_and_execute ( "[format('{1:e} = {0:E}', true, 13)]" , & Context :: new ( ) ) ;
239
+ assert ! ( result. is_err( ) ) ;
116
240
}
117
241
242
+ #[ test]
243
+ fn args_out_of_bounds ( ) {
244
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
245
+ let result = parser. parse_and_execute ( "[format('hello {1} {2} there', 12, 13)]" , & Context :: new ( ) ) ;
246
+ assert ! ( result. is_err( ) ) ;
247
+ }
248
+
249
+ #[ test]
250
+ fn missing_closing_brace ( ) {
251
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
252
+ let result = parser. parse_and_execute ( "[format('hello {0 there', 12, 13)]" , & Context :: new ( ) ) ;
253
+ assert ! ( result. is_err( ) ) ;
254
+ }
255
+
256
+ #[ test]
257
+ fn missing_opening_brace ( ) {
258
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
259
+ let result = parser. parse_and_execute ( "[format('hello 0} there', 12, 13)]" , & Context :: new ( ) ) ;
260
+ assert ! ( result. is_err( ) ) ;
261
+ }
262
+
263
+ #[ test]
264
+ fn invalid_format_option ( ) {
265
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
266
+ let result = parser. parse_and_execute ( "[format('hello {0:invalid} there', 12, 13)]" , & Context :: new ( ) ) ;
267
+ assert ! ( result. is_err( ) ) ;
268
+ }
269
+
270
+ #[ test]
271
+ fn invalid_index_syntax ( ) {
272
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
273
+ let result = parser. parse_and_execute ( "[format('hello {0;x} there', 12, 13)]" , & Context :: new ( ) ) ;
274
+ assert ! ( result. is_err( ) ) ;
275
+ }
276
+
277
+ #[ test]
278
+ fn missing_format_type ( ) {
279
+ let mut parser = Statement :: new ( ) . unwrap ( ) ;
280
+ let result = parser. parse_and_execute ( "[format('hello {0:} there', 12, 13)]" , & Context :: new ( ) ) . unwrap ( ) ;
281
+ assert_eq ! ( result, "hello 12 there" ) ;
282
+ }
118
283
}
0 commit comments