sprintf/
format.rs

1use std::convert::{TryFrom, TryInto};
2use std::ffi::{CStr, CString};
3
4use crate::{
5    parser::{ConversionSpecifier, ConversionType, NumericParam},
6    PrintfError, Result,
7};
8
9/// Trait for types that can be formatted using printf strings
10///
11/// Implemented for the basic types and shouldn't need implementing for
12/// anything else.
13pub trait Printf {
14    /// Format `self` based on the conversion configured in `spec`.
15    fn format(&self, spec: &ConversionSpecifier) -> Result<String>;
16    /// Get `self` as an integer for use as a field width, if possible.
17    fn as_int(&self) -> Option<i32>;
18}
19
20impl Printf for u64 {
21    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
22        let mut base = 10;
23        let mut digits: Vec<char> = "0123456789".chars().collect();
24        let mut alt_prefix = "";
25        match spec.conversion_type {
26            ConversionType::DecInt => {}
27            ConversionType::HexIntLower => {
28                base = 16;
29                digits = "0123456789abcdef".chars().collect();
30                alt_prefix = "0x";
31            }
32            ConversionType::HexIntUpper => {
33                base = 16;
34                digits = "0123456789ABCDEF".chars().collect();
35                alt_prefix = "0X";
36            }
37            ConversionType::OctInt => {
38                base = 8;
39                digits = "01234567".chars().collect();
40                alt_prefix = "0";
41            }
42            _ => {
43                return Err(PrintfError::WrongType);
44            }
45        }
46        let prefix = if spec.alt_form {
47            alt_prefix.to_owned()
48        } else {
49            String::new()
50        };
51
52        // Build the actual number (in reverse)
53        let mut rev_num = String::new();
54        let mut n = *self;
55        while n > 0 {
56            let digit = n % base;
57            n /= base;
58            rev_num.push(digits[digit as usize]);
59        }
60        if rev_num.is_empty() {
61            rev_num.push('0');
62        }
63
64        // Take care of padding
65        let width: usize = match spec.width {
66            NumericParam::Literal(w) => w,
67            _ => {
68                return Err(PrintfError::Unknown); // should not happen at this point!!
69            }
70        }
71        .try_into()
72        .unwrap_or_default();
73        let formatted = if spec.left_adj {
74            let mut num_str = prefix + &rev_num.chars().rev().collect::<String>();
75            while num_str.len() < width {
76                num_str.push(' ');
77            }
78            num_str
79        } else if spec.zero_pad {
80            while prefix.len() + rev_num.len() < width {
81                rev_num.push('0');
82            }
83            prefix + &rev_num.chars().rev().collect::<String>()
84        } else {
85            let mut num_str = prefix + &rev_num.chars().rev().collect::<String>();
86            while num_str.len() < width {
87                num_str = " ".to_owned() + &num_str;
88            }
89            num_str
90        };
91
92        Ok(formatted)
93    }
94    fn as_int(&self) -> Option<i32> {
95        i32::try_from(*self).ok()
96    }
97}
98
99impl Printf for i64 {
100    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
101        match spec.conversion_type {
102            // signed integer format
103            ConversionType::DecInt => {
104                // do I need a sign prefix?
105                let negative = *self < 0;
106                let abs_val = self.abs();
107                let sign_prefix = if negative {
108                    "-"
109                } else if spec.force_sign {
110                    "+"
111                } else if spec.space_sign {
112                    " "
113                } else {
114                    ""
115                }
116                .to_owned();
117                let mut mod_spec = *spec;
118                mod_spec.width = match spec.width {
119                    NumericParam::Literal(w) => NumericParam::Literal(w - sign_prefix.len() as i32),
120                    _ => {
121                        return Err(PrintfError::Unknown);
122                    }
123                };
124
125                let formatted = (abs_val as u64).format(&mod_spec)?;
126                // put the sign a after any leading spaces
127                let mut actual_number = &formatted[0..];
128                let mut leading_spaces = &formatted[0..0];
129                if let Some(first_non_space) = formatted.find(|c| c != ' ') {
130                    actual_number = &formatted[first_non_space..];
131                    leading_spaces = &formatted[0..first_non_space];
132                }
133                Ok(leading_spaces.to_owned() + &sign_prefix + actual_number)
134            }
135            // unsigned-only formats
136            ConversionType::HexIntLower | ConversionType::HexIntUpper | ConversionType::OctInt => {
137                (*self as u64).format(spec)
138            }
139            _ => Err(PrintfError::WrongType),
140        }
141    }
142    fn as_int(&self) -> Option<i32> {
143        i32::try_from(*self).ok()
144    }
145}
146
147impl Printf for i32 {
148    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
149        match spec.conversion_type {
150            // signed integer format
151            ConversionType::DecInt => (*self as i64).format(spec),
152            // unsigned-only formats
153            ConversionType::HexIntLower | ConversionType::HexIntUpper | ConversionType::OctInt => {
154                (*self as u32).format(spec)
155            }
156            _ => Err(PrintfError::WrongType),
157        }
158    }
159    fn as_int(&self) -> Option<i32> {
160        Some(*self)
161    }
162}
163
164impl Printf for u32 {
165    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
166        match spec.conversion_type {
167            ConversionType::Char => {
168                if let Some(c) = char::from_u32(*self) {
169                    c.format(spec)
170                } else {
171                    Err(PrintfError::WrongType)
172                }
173            }
174            _ => (*self as u64).format(spec),
175        }
176    }
177    fn as_int(&self) -> Option<i32> {
178        i32::try_from(*self).ok()
179    }
180}
181
182impl Printf for i16 {
183    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
184        match spec.conversion_type {
185            // signed integer format
186            ConversionType::DecInt => (*self as i64).format(spec),
187            // unsigned-only formats
188            ConversionType::HexIntLower | ConversionType::HexIntUpper | ConversionType::OctInt => {
189                (*self as u16).format(spec)
190            }
191            _ => Err(PrintfError::WrongType),
192        }
193    }
194    fn as_int(&self) -> Option<i32> {
195        Some(*self as i32)
196    }
197}
198
199impl Printf for u16 {
200    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
201        match spec.conversion_type {
202            ConversionType::Char => {
203                if let Some(Ok(c)) = char::decode_utf16([*self]).next() {
204                    c.format(spec)
205                } else {
206                    Err(PrintfError::WrongType)
207                }
208            }
209            _ => (*self as u64).format(spec),
210        }
211    }
212    fn as_int(&self) -> Option<i32> {
213        Some(*self as i32)
214    }
215}
216
217impl Printf for i8 {
218    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
219        match spec.conversion_type {
220            // signed integer format
221            ConversionType::DecInt => (*self as i64).format(spec),
222            // unsigned-only formats
223            ConversionType::HexIntLower | ConversionType::HexIntUpper | ConversionType::OctInt => {
224                (*self as u8).format(spec)
225            }
226            // c_char
227            ConversionType::Char => (*self as u8).format(spec),
228            _ => Err(PrintfError::WrongType),
229        }
230    }
231    fn as_int(&self) -> Option<i32> {
232        Some(*self as i32)
233    }
234}
235
236impl Printf for u8 {
237    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
238        match spec.conversion_type {
239            ConversionType::Char => {
240                if self.is_ascii() {
241                    char::from(*self).format(spec)
242                } else {
243                    Err(PrintfError::WrongType)
244                }
245            }
246            _ => (*self as u64).format(spec),
247        }
248    }
249    fn as_int(&self) -> Option<i32> {
250        Some(*self as i32)
251    }
252}
253
254impl Printf for usize {
255    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
256        (*self as u64).format(spec)
257    }
258    fn as_int(&self) -> Option<i32> {
259        i32::try_from(*self).ok()
260    }
261}
262
263impl Printf for isize {
264    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
265        (*self as u64).format(spec)
266    }
267    fn as_int(&self) -> Option<i32> {
268        i32::try_from(*self).ok()
269    }
270}
271
272impl Printf for f64 {
273    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
274        let mut prefix = String::new();
275        let mut number = String::new();
276
277        // set up the sign
278        if self.is_sign_negative() {
279            prefix.push('-');
280        } else if spec.space_sign {
281            prefix.push(' ');
282        } else if spec.force_sign {
283            prefix.push('+');
284        }
285
286        if self.is_finite() {
287            let mut use_scientific = false;
288            let mut exp_symb = 'e';
289            let mut strip_trailing_0s = false;
290            let mut abs = self.abs();
291            let mut exponent = abs.log10().floor() as i32;
292            let mut precision = match spec.precision {
293                NumericParam::Literal(p) => p,
294                _ => {
295                    return Err(PrintfError::Unknown);
296                }
297            };
298            if precision <= 0 {
299                precision = 0;
300            }
301            match spec.conversion_type {
302                ConversionType::DecFloatLower | ConversionType::DecFloatUpper => {
303                    // default
304                }
305                ConversionType::SciFloatLower => {
306                    use_scientific = true;
307                }
308                ConversionType::SciFloatUpper => {
309                    use_scientific = true;
310                    exp_symb = 'E';
311                }
312                ConversionType::CompactFloatLower | ConversionType::CompactFloatUpper => {
313                    if spec.conversion_type == ConversionType::CompactFloatUpper {
314                        exp_symb = 'E'
315                    }
316                    strip_trailing_0s = true;
317                    if precision == 0 {
318                        precision = 1;
319                    }
320                    // exponent signifies significant digits - we must round now
321                    // to (re)calculate the exponent
322                    let rounding_factor = 10.0_f64.powf((precision - 1 - exponent) as f64);
323                    let rounded_fixed = (abs * rounding_factor).round();
324                    abs = rounded_fixed / rounding_factor;
325                    exponent = abs.log10().floor() as i32;
326                    if exponent < -4 || exponent >= precision {
327                        use_scientific = true;
328                        precision -= 1;
329                    } else {
330                        // precision specifies the number of significant digits
331                        precision -= 1 + exponent;
332                    }
333                }
334                _ => {
335                    return Err(PrintfError::WrongType);
336                }
337            }
338
339            if use_scientific {
340                let mut normal = abs / 10.0_f64.powf(exponent as f64);
341
342                if precision > 0 {
343                    let mut int_part = normal.trunc();
344                    let mut exp_factor = 10.0_f64.powf(precision as f64);
345                    let mut tail = ((normal - int_part) * exp_factor).round() as u64;
346                    while tail >= exp_factor as u64 {
347                        // Overflow, must round
348                        int_part += 1.0;
349                        tail -= exp_factor as u64;
350                        if int_part >= 10.0 {
351                            // keep same precision - which means changing exponent
352                            exponent += 1;
353                            exp_factor /= 10.0;
354                            normal /= 10.0;
355                            int_part = normal.trunc();
356                            tail = ((normal - int_part) * exp_factor).round() as u64;
357                        }
358                    }
359
360                    let mut rev_tail_str = String::new();
361                    for _ in 0..precision {
362                        rev_tail_str.push((b'0' + (tail % 10) as u8) as char);
363                        tail /= 10;
364                    }
365                    number.push_str(&format!("{}", int_part));
366                    number.push('.');
367                    number.push_str(&rev_tail_str.chars().rev().collect::<String>());
368                    if strip_trailing_0s {
369                        number = number.trim_end_matches('0').to_owned();
370                    }
371                } else {
372                    number.push_str(&format!("{}", normal.round()));
373                }
374                number.push(exp_symb);
375                number.push_str(&format!("{:+03}", exponent));
376            } else {
377                if precision > 0 {
378                    let mut int_part = abs.trunc();
379                    let exp_factor = 10.0_f64.powf(precision as f64);
380                    let mut tail = ((abs - int_part) * exp_factor).round() as u64;
381                    let mut rev_tail_str = String::new();
382                    if tail >= exp_factor as u64 {
383                        // overflow - we must round up
384                        int_part += 1.0;
385                        tail -= exp_factor as u64;
386                        // no need to change the exponent as we don't have one
387                        // (not scientific notation)
388                    }
389                    for _ in 0..precision {
390                        rev_tail_str.push((b'0' + (tail % 10) as u8) as char);
391                        tail /= 10;
392                    }
393                    number.push_str(&format!("{}", int_part));
394                    number.push('.');
395                    number.push_str(&rev_tail_str.chars().rev().collect::<String>());
396                    if strip_trailing_0s {
397                        number = number.trim_end_matches('0').to_owned();
398                    }
399                } else {
400                    number.push_str(&format!("{}", abs.round()));
401                }
402            }
403        } else {
404            // not finite
405            match spec.conversion_type {
406                ConversionType::DecFloatLower
407                | ConversionType::SciFloatLower
408                | ConversionType::CompactFloatLower => {
409                    if self.is_infinite() {
410                        number.push_str("inf")
411                    } else {
412                        number.push_str("nan")
413                    }
414                }
415                ConversionType::DecFloatUpper
416                | ConversionType::SciFloatUpper
417                | ConversionType::CompactFloatUpper => {
418                    if self.is_infinite() {
419                        number.push_str("INF")
420                    } else {
421                        number.push_str("NAN")
422                    }
423                }
424                _ => {
425                    return Err(PrintfError::WrongType);
426                }
427            }
428        }
429        // Take care of padding
430        let width: usize = match spec.width {
431            NumericParam::Literal(w) => w,
432            _ => {
433                return Err(PrintfError::Unknown); // should not happen at this point!!
434            }
435        }
436        .try_into()
437        .unwrap_or_default();
438        let formatted = if spec.left_adj {
439            let mut full_num = prefix + &number;
440            while full_num.len() < width {
441                full_num.push(' ');
442            }
443            full_num
444        } else if spec.zero_pad && self.is_finite() {
445            while prefix.len() + number.len() < width {
446                prefix.push('0');
447            }
448            prefix + &number
449        } else {
450            let mut full_num = prefix + &number;
451            while full_num.len() < width {
452                full_num = " ".to_owned() + &full_num;
453            }
454            full_num
455        };
456        Ok(formatted)
457    }
458    fn as_int(&self) -> Option<i32> {
459        None
460    }
461}
462
463impl Printf for f32 {
464    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
465        (*self as f64).format(spec)
466    }
467    fn as_int(&self) -> Option<i32> {
468        None
469    }
470}
471
472impl Printf for &str {
473    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
474        if spec.conversion_type == ConversionType::String {
475            let mut s = String::new();
476
477            // Take care of precision, putting the truncated string in `content`
478            let precision: usize = match spec.precision {
479                NumericParam::Literal(p) => p,
480                _ => {
481                    return Err(PrintfError::Unknown); // should not happen at this point!!
482                }
483            }
484            .try_into()
485            .unwrap_or_default();
486            let content_len = {
487                let mut content_len = precision.min(self.len());
488                while !self.is_char_boundary(content_len) {
489                    content_len -= 1;
490                }
491                content_len
492            };
493            let content = &self[..content_len];
494
495            // Pad to width if needed, putting the padded string in `s`
496            let width: usize = match spec.width {
497                NumericParam::Literal(w) => w,
498                _ => {
499                    return Err(PrintfError::Unknown); // should not happen at this point!!
500                }
501            }
502            .try_into()
503            .unwrap_or_default();
504            if spec.left_adj {
505                s.push_str(content);
506                while s.len() < width {
507                    s.push(' ');
508                }
509            } else {
510                while s.len() + content.len() < width {
511                    s.push(' ');
512                }
513                s.push_str(content);
514            }
515
516            Ok(s)
517        } else {
518            Err(PrintfError::WrongType)
519        }
520    }
521    fn as_int(&self) -> Option<i32> {
522        None
523    }
524}
525
526impl Printf for char {
527    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
528        if spec.conversion_type == ConversionType::Char {
529            let mut s = String::new();
530
531            let width: usize = match spec.width {
532                NumericParam::Literal(w) => w,
533                _ => {
534                    return Err(PrintfError::Unknown); // should not happen at this point!!
535                }
536            }
537            .try_into()
538            .unwrap_or_default();
539
540            if spec.left_adj {
541                s.push(*self);
542                while s.len() < width {
543                    s.push(' ');
544                }
545            } else {
546                while s.len() + self.len_utf8() < width {
547                    s.push(' ');
548                }
549                s.push(*self);
550            }
551            Ok(s)
552        } else {
553            Err(PrintfError::WrongType)
554        }
555    }
556    fn as_int(&self) -> Option<i32> {
557        None
558    }
559}
560
561impl Printf for String {
562    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
563        (self as &str).format(spec)
564    }
565    fn as_int(&self) -> Option<i32> {
566        None
567    }
568}
569
570impl Printf for &CStr {
571    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
572        if let Ok(s) = self.to_str() {
573            s.format(spec)
574        } else {
575            Err(PrintfError::WrongType)
576        }
577    }
578    fn as_int(&self) -> Option<i32> {
579        None
580    }
581}
582
583impl Printf for CString {
584    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
585        self.as_c_str().format(spec)
586    }
587    fn as_int(&self) -> Option<i32> {
588        None
589    }
590}
591
592impl<T> Printf for *const T {
593    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
594        (*self as usize).format(spec)
595    }
596    fn as_int(&self) -> Option<i32> {
597        None
598    }
599}
600
601impl<T> Printf for *mut T {
602    fn format(&self, spec: &ConversionSpecifier) -> Result<String> {
603        (*self as usize).format(spec)
604    }
605    fn as_int(&self) -> Option<i32> {
606        None
607    }
608}