1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! Format numbers according to a runtime specification.
//!
//! ## Entry Points
//!
//! The core of this crate is the [`NumFmt`] type. You can [build][NumFmt::builder] it explicitly,
//! or [parse][NumFmt::from_str] it from a format string with similar grammar to that of the
//! standard library.
//!
//! Given an instance of `NumFmt`, you can call its [`fmt`][NumFmt::fmt] method to simply format
//! a number, or its [`fmt_with`][NumFmt::fmt_with] method to apply dynamic parameters.
//!
//! ## Format String Grammar
//!
//! The gramar for the format string derives substantially from the standard library's:
//!
//! ```text
//! format_spec := [[fill]align][sign]['#'][['0']width]['.' precision][format][separator[spacing]]
//! fill := character
//! align := '<' | '^' | '>' | 'v'
//! sign := '+' | '-'
//! width := integer not beginning with '0'
//! precision := integer
//! format := 'b' | 'o' | 'd' | 'x' | 'X'
//! separator := '_', | ',' | ' '
//! spacing := integer
//! ```
//!
//! ### Note
//!
//! There is no special syntax for dynamic insertion of `with`, `precision` and `spacing`.
//! Simply use [`NumFmt::fmt_with`]; the dynamic values provided there always override any
//! values for those fields, whether set or not in the format string.
//!
//! ## `fill`
//!
//! Any single `char` which precedes an align specifier is construed as the fill
//! character: when `width` is greater than the actual rendered width of the number,
//! the excess is padded with this character.
//!
//! ### Note
//! Wide characters are counted according to their quantity, not their bit width.
//!
//! ```rust
//! # use num_runtime_fmt::NumFmt;
//! let heart = '🖤';
//! assert_eq!(heart.len_utf8(), 4);
//! let fmt = NumFmt::builder().fill(heart).width(3).build();
//! let formatted = fmt.fmt(1).unwrap();
//! assert_eq!(formatted, "🖤🖤1");
//! // Note that even though we requested a width of 3, the binary length is 9.
//! assert_eq!(formatted.len(), 9);
//! ```
//!
//! ## `align`ment
//!
//! - `>`: the output is right-aligned in `width` columns (default).
//! - `^`: the output is centered in `width` columns.
//! - `<`: the output is left-aligned in `width` columns.
//! - `v`: attempt to align the decimal point at column index `width`. For integers,
//!   equivalent to `>`.
//!
//! ## `sign`
//!
//! - `-`: print a leading `-` for negative numbers, and nothing in particular for
//!   positive (default)
//! - `+`: print a leading `+` for positive numbers
//!
//! ## `#`
//!
//! If a `#` character is present, print a base specification before the number
//! according to its format (see `format` below).
//!
//! - binary: `0b`
//! - octal: `0o`
//! - decimal: `0d`
//! - hex: `0x`
//!
//! This base specification counts toward the width of the number:
//!
//! ```rust
//! # use num_runtime_fmt::NumFmt;
//! assert_eq!(NumFmt::from_str("#04b").unwrap().fmt(2).unwrap(), "0b10");
//! ```
//!
//! ## `0`
//!
//! Engage the zero handler.
//!
//! The zero handler overrides the padding specification to `0`, and
//! treats pad characters as part of the number, in contrast
//! to the default behavior which treats them as arbitrary spacing.
//!
//! ## Examples
//!
//! ```rust
//! # use num_runtime_fmt::NumFmt;
//! // sign handling
//! assert_eq!(NumFmt::from_str("03").unwrap().fmt(-1).unwrap(),  "-01");
//! assert_eq!(NumFmt::from_str("0>3").unwrap().fmt(-1).unwrap(), "0-1");
//! ```
//!
//! ```rust
//! # use num_runtime_fmt::NumFmt;
//! // separator handling
//! assert_eq!(NumFmt::from_str("0>7,").unwrap().fmt(1).unwrap(), "0000001");
//! assert_eq!(NumFmt::from_str("07,").unwrap().fmt(1).unwrap(),  "000,001");
//! ```
//!
//! ## `width`
//!
//! This is a parameter for the "minimum width" that the format should take up. If
//! the value's string does not fill up this many characters, then the padding
//! specified by fill/alignment will be used to take up the required space (see
//! `fill` above).
//!
//! When using the `$` sigil instead of an explicit width, the width can be set
//! dynamically:
//!
//! ```rust
//! # use num_runtime_fmt::{NumFmt, Dynamic};
//! assert_eq!(NumFmt::from_str("-^").unwrap().fmt_with(1, Dynamic::width(5)).unwrap(), "--1--");
//! ```
//!
//! If an explicit width is not provided, defaults to 0.
//!
//! ## `precision`
//!
//! Precision will pad or truncate as required if set. If unset, passes through as many
//! digits past the decimal as the underlying type naturally returns.
//!
//! ```rust
//! # use num_runtime_fmt::{NumFmt, Dynamic};
//! assert_eq!(NumFmt::from_str(".2").unwrap().fmt(3.14159).unwrap(), "3.14");
//! assert_eq!(NumFmt::from_str(".7").unwrap().fmt(3.14159).unwrap(), "3.1415900");
//! ```
//!
//! If the requested precision exceeds the native precision available to this number,
//! the remainder is always filled with `'0'`, even if `fill` is specified:
//!
//! ```rust
//! # use num_runtime_fmt::NumFmt;
//! assert_eq!(NumFmt::from_str("-<6.2").unwrap().fmt(1.0_f32).unwrap(), "1.00--");
//! ```
//!
//! ## `format`
//!
//! - `b`: Emit this number's binary representation
//! - `o`: Emit this number's octal representation
//! - `d`: Emit this number's decimal representation (default)
//! - `x`: Emit this number's hexadecimal representation with lowercase letters
//! - `X`: Emit this number's hexadecimal representation with uppercase letters
//!
//! ### Note
//!
//! This is one of a few areas where the standard library has
//! capabilities this library does not: it supports some other numeric formats.
//! Pull requests welcomed to bring this up to parity.
//!
//! ## `separator`
//!
//! A separator is a (typically non-numeric) character inserted between groups of digits to make
//! it easier for humans to parse the number when reading. Different separators may
//! be desirable in different contexts.
//!
//! - `_`: Separate numeric groups with an underscore
//! - `,`: Separate numeric groups with a comma
//! - ` ` (space char): Separate numeric groups with a space
//!
//! By default, numeric groups are not separated. It is not possible to explicitly
//! specify that numeric groups are not separated when using a format string.
//! However, this can be specified when building the formatter via builder.
//!
//! When using the builder to explicitly set formatter options, it is also possible
//! to separate numeric groups with an arbitrary `char`. This can be desirable to
//! i.e. support German number formats, which use a `.` to separate numeric groups
//! and a `,` as a decimal separator.
//!
//! ## `spacing`
//!
//! Spacing determines the number of characters in each character group. It is only
//! of interest when the separator is set. The default spacing is 3.

mod align;
mod base;
mod builder;
mod dynamic;
mod num_fmt;
pub mod numeric_trait;
pub mod parse;
mod sign;

pub use align::Align;
pub use base::Base;
pub use builder::Builder;
pub use dynamic::Dynamic;
pub use num_fmt::{Error, NumFmt};
pub use numeric_trait::Numeric;
pub use sign::Sign;