6
6
use super :: ExtendedBigDecimal ;
7
7
use crate :: format:: spec:: ArgumentLocation ;
8
8
use crate :: {
9
- error:: set_exit_code,
9
+ error:: { UResult , USimpleError , set_exit_code} ,
10
10
parser:: num_parser:: { ExtendedParser , ExtendedParserError } ,
11
11
quoting_style:: { Quotes , QuotingStyle , escape_name} ,
12
- show_error, show_warning,
12
+ show , show_error, show_warning,
13
13
} ;
14
14
use os_display:: Quotable ;
15
- use std:: { ffi:: OsStr , num:: NonZero } ;
15
+ use std:: {
16
+ ffi:: { OsStr , OsString } ,
17
+ num:: NonZero ,
18
+ } ;
16
19
17
20
/// An argument for formatting
18
21
///
@@ -24,12 +27,12 @@ use std::{ffi::OsStr, num::NonZero};
24
27
#[ derive( Clone , Debug , PartialEq ) ]
25
28
pub enum FormatArgument {
26
29
Char ( char ) ,
27
- String ( String ) ,
30
+ String ( OsString ) ,
28
31
UnsignedInt ( u64 ) ,
29
32
SignedInt ( i64 ) ,
30
33
Float ( ExtendedBigDecimal ) ,
31
34
/// Special argument that gets coerced into the other variants
32
- Unparsed ( String ) ,
35
+ Unparsed ( OsString ) ,
33
36
}
34
37
35
38
/// A struct that holds a slice of format arguments and provides methods to access them
@@ -72,30 +75,38 @@ impl<'a> FormatArguments<'a> {
72
75
pub fn next_char ( & mut self , position : & ArgumentLocation ) -> u8 {
73
76
match self . next_arg ( position) {
74
77
Some ( FormatArgument :: Char ( c) ) => * c as u8 ,
75
- Some ( FormatArgument :: Unparsed ( s) ) => s. bytes ( ) . next ( ) . unwrap_or ( b'\0' ) ,
78
+ Some ( FormatArgument :: Unparsed ( os) ) => match bytes_from_os_str ( os) . unwrap ( ) . first ( ) {
79
+ Some ( & byte) => byte,
80
+ None => b'\0' ,
81
+ } ,
76
82
_ => b'\0' ,
77
83
}
78
84
}
79
85
80
- pub fn next_string ( & mut self , position : & ArgumentLocation ) -> & ' a str {
86
+ pub fn next_string ( & mut self , position : & ArgumentLocation ) -> & ' a OsStr {
81
87
match self . next_arg ( position) {
82
- Some ( FormatArgument :: Unparsed ( s ) | FormatArgument :: String ( s ) ) => s ,
83
- _ => "" ,
88
+ Some ( FormatArgument :: Unparsed ( os ) | FormatArgument :: String ( os ) ) => os ,
89
+ _ => "" . as_ref ( ) ,
84
90
}
85
91
}
86
92
87
93
pub fn next_i64 ( & mut self , position : & ArgumentLocation ) -> i64 {
88
94
match self . next_arg ( position) {
89
95
Some ( FormatArgument :: SignedInt ( n) ) => * n,
90
- Some ( FormatArgument :: Unparsed ( s) ) => extract_value ( i64:: extended_parse ( s) , s) ,
96
+ Some ( FormatArgument :: Unparsed ( os) ) => {
97
+ let str = get_str_or_exit_with_error ( os) ;
98
+
99
+ extract_value ( i64:: extended_parse ( str) , str)
100
+ }
91
101
_ => 0 ,
92
102
}
93
103
}
94
104
95
105
pub fn next_u64 ( & mut self , position : & ArgumentLocation ) -> u64 {
96
106
match self . next_arg ( position) {
97
107
Some ( FormatArgument :: UnsignedInt ( n) ) => * n,
98
- Some ( FormatArgument :: Unparsed ( s) ) => {
108
+ Some ( FormatArgument :: Unparsed ( os) ) => {
109
+ let s = get_str_or_exit_with_error ( os) ;
99
110
// Check if the string is a character literal enclosed in quotes
100
111
if s. starts_with ( [ '"' , '\'' ] ) {
101
112
// Extract the content between the quotes safely using chars
@@ -122,7 +133,9 @@ impl<'a> FormatArguments<'a> {
122
133
pub fn next_extended_big_decimal ( & mut self , position : & ArgumentLocation ) -> ExtendedBigDecimal {
123
134
match self . next_arg ( position) {
124
135
Some ( FormatArgument :: Float ( n) ) => n. clone ( ) ,
125
- Some ( FormatArgument :: Unparsed ( s) ) => {
136
+ Some ( FormatArgument :: Unparsed ( os) ) => {
137
+ let s = get_str_or_exit_with_error ( os) ;
138
+
126
139
extract_value ( ExtendedBigDecimal :: extended_parse ( s) , s)
127
140
}
128
141
_ => ExtendedBigDecimal :: zero ( ) ,
@@ -188,6 +201,53 @@ fn extract_value<T: Default>(p: Result<T, ExtendedParserError<'_, T>>, input: &s
188
201
}
189
202
}
190
203
204
+ pub fn bytes_from_os_str ( input : & OsStr ) -> UResult < & [ u8 ] > {
205
+ let result = {
206
+ #[ cfg( target_family = "unix" ) ]
207
+ {
208
+ use std:: os:: unix:: ffi:: OsStrExt ;
209
+
210
+ Ok ( input. as_bytes ( ) )
211
+ }
212
+
213
+ #[ cfg( not( target_family = "unix" ) ) ]
214
+ {
215
+ use crate :: error:: USimpleError ;
216
+
217
+ // TODO
218
+ // Verify that this works correctly on these platforms
219
+ match input. to_str ( ) . map ( |st| st. as_bytes ( ) ) {
220
+ Some ( sl) => Ok ( sl) ,
221
+ None => Err ( USimpleError :: new (
222
+ 1 ,
223
+ "non-UTF-8 string encountered when not allowed" ,
224
+ ) ) ,
225
+ }
226
+ }
227
+ } ;
228
+
229
+ result
230
+ }
231
+
232
+ fn get_str_or_exit_with_error ( os_str : & OsStr ) -> & str {
233
+ match os_str. to_str ( ) {
234
+ Some ( st) => st,
235
+ None => {
236
+ let cow = os_str. to_string_lossy ( ) ;
237
+
238
+ let quoted = cow. quote ( ) ;
239
+
240
+ let error = format ! (
241
+ "argument like {quoted} is not a valid UTF-8 string, and could not be parsed as an integer" ,
242
+ ) ;
243
+
244
+ show ! ( USimpleError :: new( 1 , error. clone( ) ) ) ;
245
+
246
+ panic ! ( "{error}" ) ;
247
+ }
248
+ }
249
+ }
250
+
191
251
#[ cfg( test) ]
192
252
mod tests {
193
253
use super :: * ;
@@ -255,11 +315,11 @@ mod tests {
255
315
// Test with different method types in sequence
256
316
let args = [
257
317
FormatArgument :: Char ( 'a' ) ,
258
- FormatArgument :: String ( "hello" . to_string ( ) ) ,
259
- FormatArgument :: Unparsed ( "123" . to_string ( ) ) ,
260
- FormatArgument :: String ( "world" . to_string ( ) ) ,
318
+ FormatArgument :: String ( "hello" . into ( ) ) ,
319
+ FormatArgument :: Unparsed ( "123" . into ( ) ) ,
320
+ FormatArgument :: String ( "world" . into ( ) ) ,
261
321
FormatArgument :: Char ( 'z' ) ,
262
- FormatArgument :: String ( "test" . to_string ( ) ) ,
322
+ FormatArgument :: String ( "test" . into ( ) ) ,
263
323
] ;
264
324
let mut args = FormatArguments :: new ( & args) ;
265
325
@@ -390,10 +450,10 @@ mod tests {
390
450
fn test_unparsed_arguments ( ) {
391
451
// Test with unparsed arguments that get coerced
392
452
let args = [
393
- FormatArgument :: Unparsed ( "hello" . to_string ( ) ) ,
394
- FormatArgument :: Unparsed ( "123" . to_string ( ) ) ,
395
- FormatArgument :: Unparsed ( "hello" . to_string ( ) ) ,
396
- FormatArgument :: Unparsed ( "456" . to_string ( ) ) ,
453
+ FormatArgument :: Unparsed ( "hello" . into ( ) ) ,
454
+ FormatArgument :: Unparsed ( "123" . into ( ) ) ,
455
+ FormatArgument :: Unparsed ( "hello" . into ( ) ) ,
456
+ FormatArgument :: Unparsed ( "456" . into ( ) ) ,
397
457
] ;
398
458
let mut args = FormatArguments :: new ( & args) ;
399
459
@@ -415,10 +475,10 @@ mod tests {
415
475
// Test with mixed types and positional access
416
476
let args = [
417
477
FormatArgument :: Char ( 'a' ) ,
418
- FormatArgument :: String ( "test" . to_string ( ) ) ,
478
+ FormatArgument :: String ( "test" . into ( ) ) ,
419
479
FormatArgument :: UnsignedInt ( 42 ) ,
420
480
FormatArgument :: Char ( 'b' ) ,
421
- FormatArgument :: String ( "more" . to_string ( ) ) ,
481
+ FormatArgument :: String ( "more" . into ( ) ) ,
422
482
FormatArgument :: UnsignedInt ( 99 ) ,
423
483
] ;
424
484
let mut args = FormatArguments :: new ( & args) ;
0 commit comments