66use super :: ExtendedBigDecimal ;
77use crate :: format:: spec:: ArgumentLocation ;
88use crate :: {
9- error:: set_exit_code,
9+ error:: { UResult , USimpleError , set_exit_code} ,
1010 parser:: num_parser:: { ExtendedParser , ExtendedParserError } ,
1111 quoting_style:: { Quotes , QuotingStyle , escape_name} ,
12- show_error, show_warning,
12+ show , show_error, show_warning,
1313} ;
1414use os_display:: Quotable ;
15- use std:: { ffi:: OsStr , num:: NonZero } ;
15+ use std:: {
16+ ffi:: { OsStr , OsString } ,
17+ num:: NonZero ,
18+ } ;
1619
1720/// An argument for formatting
1821///
@@ -24,12 +27,12 @@ use std::{ffi::OsStr, num::NonZero};
2427#[ derive( Clone , Debug , PartialEq ) ]
2528pub enum FormatArgument {
2629 Char ( char ) ,
27- String ( String ) ,
30+ String ( OsString ) ,
2831 UnsignedInt ( u64 ) ,
2932 SignedInt ( i64 ) ,
3033 Float ( ExtendedBigDecimal ) ,
3134 /// Special argument that gets coerced into the other variants
32- Unparsed ( String ) ,
35+ Unparsed ( OsString ) ,
3336}
3437
3538/// A struct that holds a slice of format arguments and provides methods to access them
@@ -72,30 +75,38 @@ impl<'a> FormatArguments<'a> {
7275 pub fn next_char ( & mut self , position : & ArgumentLocation ) -> u8 {
7376 match self . next_arg ( position) {
7477 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+ } ,
7682 _ => b'\0' ,
7783 }
7884 }
7985
80- pub fn next_string ( & mut self , position : & ArgumentLocation ) -> & ' a str {
86+ pub fn next_string ( & mut self , position : & ArgumentLocation ) -> & ' a OsStr {
8187 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 ( ) ,
8490 }
8591 }
8692
8793 pub fn next_i64 ( & mut self , position : & ArgumentLocation ) -> i64 {
8894 match self . next_arg ( position) {
8995 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+ }
91101 _ => 0 ,
92102 }
93103 }
94104
95105 pub fn next_u64 ( & mut self , position : & ArgumentLocation ) -> u64 {
96106 match self . next_arg ( position) {
97107 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) ;
99110 // Check if the string is a character literal enclosed in quotes
100111 if s. starts_with ( [ '"' , '\'' ] ) {
101112 // Extract the content between the quotes safely using chars
@@ -122,7 +133,9 @@ impl<'a> FormatArguments<'a> {
122133 pub fn next_extended_big_decimal ( & mut self , position : & ArgumentLocation ) -> ExtendedBigDecimal {
123134 match self . next_arg ( position) {
124135 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+
126139 extract_value ( ExtendedBigDecimal :: extended_parse ( s) , s)
127140 }
128141 _ => ExtendedBigDecimal :: zero ( ) ,
@@ -188,6 +201,53 @@ fn extract_value<T: Default>(p: Result<T, ExtendedParserError<'_, T>>, input: &s
188201 }
189202}
190203
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+
191251#[ cfg( test) ]
192252mod tests {
193253 use super :: * ;
@@ -255,11 +315,11 @@ mod tests {
255315 // Test with different method types in sequence
256316 let args = [
257317 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 ( ) ) ,
261321 FormatArgument :: Char ( 'z' ) ,
262- FormatArgument :: String ( "test" . to_string ( ) ) ,
322+ FormatArgument :: String ( "test" . into ( ) ) ,
263323 ] ;
264324 let mut args = FormatArguments :: new ( & args) ;
265325
@@ -390,10 +450,10 @@ mod tests {
390450 fn test_unparsed_arguments ( ) {
391451 // Test with unparsed arguments that get coerced
392452 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 ( ) ) ,
397457 ] ;
398458 let mut args = FormatArguments :: new ( & args) ;
399459
@@ -415,10 +475,10 @@ mod tests {
415475 // Test with mixed types and positional access
416476 let args = [
417477 FormatArgument :: Char ( 'a' ) ,
418- FormatArgument :: String ( "test" . to_string ( ) ) ,
478+ FormatArgument :: String ( "test" . into ( ) ) ,
419479 FormatArgument :: UnsignedInt ( 42 ) ,
420480 FormatArgument :: Char ( 'b' ) ,
421- FormatArgument :: String ( "more" . to_string ( ) ) ,
481+ FormatArgument :: String ( "more" . into ( ) ) ,
422482 FormatArgument :: UnsignedInt ( 99 ) ,
423483 ] ;
424484 let mut args = FormatArguments :: new ( & args) ;
0 commit comments