44use crate :: DscError ;
55use crate :: configure:: context:: Context ;
66use 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 } ;
88use rust_i18n:: t;
99use serde_json:: Value ;
10- use std:: fmt;
11- use std:: fmt:: { Error , Formatter , Write } ;
1210
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 {
1527 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}" ) ,
1831 }
1932 }
2033
21- fn fmt_display ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
34+ fn fmt_octal ( & self , f : & mut std :: fmt :: Formatter ) -> std:: fmt:: Result {
2235 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 ) ,
2738 }
2839 }
2940
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 {
3142 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 ) ,
3445 }
3546 }
3647
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 {
3849 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 ) ,
4152 }
4253 }
4354
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 {
4567 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 ) ,
4870 }
4971 }
5072
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 {
5274 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 ) ,
5577 }
5678 }
5779}
@@ -69,29 +91,33 @@ impl Function for Format {
6991 }
7092
7193 fn accepted_arg_types ( & self ) -> Vec < AcceptedArgKind > {
72- vec ! [ AcceptedArgKind :: Boolean , AcceptedArgKind :: String , AcceptedArgKind :: Number ]
94+ vec ! [ AcceptedArgKind :: Boolean , AcceptedArgKind :: Number , AcceptedArgKind :: String ]
7395 }
7496
7597 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 ( ) ) ) ;
79100 } ;
101+ let mut position_args = Vec :: new ( ) ;
80102 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 )
86108 } else {
87- return Err ( DscError :: Parser ( "Invalid format specifier" . to_string ( ) ) ) ;
109+ return Err ( DscError :: Parser ( t ! ( "functions. format.numberTooLarge" ) . to_string ( ) ) ) ;
88110 }
89111 }
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 ) ;
94116 }
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+ } ;
95121 Ok ( Value :: String ( string_result) )
96122 }
97123}
@@ -104,15 +130,154 @@ mod tests {
104130 #[ test]
105131 fn position ( ) {
106132 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" ) ;
109149 }
110150
111151 #[ test]
112152 fn numbers_as_hex ( ) {
113153 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( ) ) ;
116240 }
117241
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+ }
118283}
0 commit comments