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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*!
[![Build Status](https://travis-ci.org/bcmyers/num-format.svg?branch=master)](https://travis-ci.org/bcmyers/num-format)
[![Crates.io](https://img.shields.io/crates/v/num-format.svg)](https://crates.io/crates/num-format)
[![Documentation](https://docs.rs/num-format/badge.svg)](https://docs.rs/num-format/)
![License](https://img.shields.io/crates/l/num_format.svg)

A Rust crate for producing string representations of numbers, formatted according to international
standards, e.g.

* `"1,000,000"` for US English
* `"10,00,000"` for Indian English
* `"1 000 000"` for French French

# Creating a string representation

**num-format** offers **three** principal APIs...

### `ToFormattedString`

The [`ToFormattedString`] trait is the simplist of the three APIs. Just call
[`to_formatted_string`] on a type that implements it (all the integer types in the standard library
implement it) while providing a desired format (see [picking a format] below). That said, using
[`ToFormattedString`] will always heap allocate; so it is the slowest of the three APIs and cannot
be used in a `no_std` environment.

```rust
# use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "std")] {
use num_format::{Locale, ToFormattedString};

fn main() {
    let s = 1000000.to_formatted_string(&Locale::en);
    assert_eq!(&s, "1,000,000");
}
# } else { fn main() {} } }
```

### `Buffer`

Using the [`Buffer`] type is the fastest API, as it does **not** heap allocate. Instead, the
formatted representation is written into a stack-allocated buffer. As such, you can use it in a
`no_std` environment.

Although this API is available for all the integer types in the standard library, it is **not**
available for types like [`num_bigint::BigInt`] whose maximum size cannot be known in advance.

```rust
use num_format::{Buffer, Locale};

fn main() {
    // Create a stack-allocated buffer...
    let mut buf = Buffer::default();

    // Write "1,000,000" into the buffer...
    buf.write_formatted(&1000000, &Locale::en);

    // Get a view into the buffer as a &str...
    let s = buf.as_str();

    // Do what you want with the &str...
    assert_eq!("1,000,000", s);
}
```

### `WriteFormatted`

The [`WriteFormatted`] trait is in between the other two APIs. You can write a formatted
representation into any type that implements [`WriteFormatted`] (all the types in the standard
library that implement [`io::Write`] or [`fmt::Write`] implement [`WriteFormatted`], such as
[`Vec`], [`String`], [`File`], etc.).

If you're writing a number type that can use the [`Buffer`] API, there is **no** heap allocation.
That said, the [`io::Write`] and [`fmt::Write`] machinery adds a bit of overhead; so it's faster
to use the [`Buffer`] type directly. This trait is **not** available in a `no_std` environment.

```rust
# use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "std")] {
use num_format::{Locale, WriteFormatted};

fn main() {
    // Create a writer...
    let mut writer = String::new(); // Could also be Vec::new(), File::open(...), ...

    // Write "1,000,000" into the writer...
    writer.write_formatted(&1000000, &Locale::en);

    assert_eq!(&writer, "1,000,000");
}
# } else { fn main() {} } }
```

# Picking a format

Formatting options (e.g. which thousands separator to use, what the minus sign looks like, etc.)
are represented by the [`Format`] trait. This crate offers **three** concrete implementations of
the [`Format`] trait...

### `Locale`

The [`Locale`] type is a programatically generated enum representing formatting standards from the
[Common Locale Data Repository], which is maintained by the [Unicode Consortium] and used by
Apple in macOS and iOS, by LibreOffice, by IBM in AIX, among others.

```rust
use num_format::{Grouping, Locale};

fn main() {
    let locale = Locale::en;
    assert_eq!(locale.grouping(), Grouping::Standard);
    assert_eq!(locale.minus_sign(), "-");
    assert_eq!(locale.name(), "en");
    assert_eq!(locale.separator(), ",");

    let locale2 = Locale::from_name("en").unwrap();
    assert_eq!(locale, locale2);

    let available = Locale::available_names();
    println!("All of the locale names available in the Unicode database are...");
    println!("{:#?}", available);
}
```

### `SystemLocale` *(available behind feature flag `with-system-locale`)*

The `SystemLocale` type is another type that implements [`Format`]. It allows you to access your
OS's locale information. It has a very similar API to [`Locale`] and should work on all major
operating systems (i.e. macOS, linux, the BSDs, and Windows).

<i>Since this type requires several dependencies (especially on Windows), it is behind a feature
flag. To use it, include `num-format = { version = "0.4", features = ["with-system-locale"] }`
in your `Cargo.toml`. Additionally, on Windows (but **only** on Windows), using `SystemLocale`
requires Clang 3.9 or higher.</i>

```rust
# #[cfg(all(feature = "with-system-locale", any(unix, windows)))]
use num_format::SystemLocale;

# #[cfg(all(feature = "with-system-locale", any(unix, windows)))]
fn main() {
    let locale = SystemLocale::default().unwrap();
    println!("My system's default locale is...");
    println!("{:#?}", &locale);

    let available = SystemLocale::available_names().unwrap();
    println!("My available locale names are...");
    println!("{:#?}", available);

    match SystemLocale::from_name("en_US") {
        Ok(_) => println!("My system has the 'en_US' locale."),
        Err(_) => println!("The 'en_US' locale is not included with my system."),
    }
}
# #[cfg(not(all(feature = "with-system-locale", any(unix, windows))))]
# fn main() {}
```

### `CustomFormat`

[`CustomFormat`] is the third and final type that implements [`Format`]. You can use it to build
your own custom formats.

```rust
use num_format::{Buffer, Error, CustomFormat, Grouping};

fn main() -> Result<(), Error> {
    let format = CustomFormat::builder()
        .grouping(Grouping::Indian)
        .minus_sign("🙌")
        .separator("😀")
        .build()?;

    let mut buf = Buffer::new();
    buf.write_formatted(&(-1000000), &format);
    assert_eq!("🙌10😀00😀000", buf.as_str());

    Ok(())
}
```

# Requirements

* Rust 1.31 or greater
* If you're using the `with-system-locale` feature **and** you're on Windows, Clang 3.9 or higher
  is also required. See [here](https://rust-lang.github.io/rust-bindgen/requirements.html) for
  installation instructions.

# Extra features

| Available features   | What to put in your `Cargo.toml`                                      |
| :------------------- | :-------------------------------------------------------------------- |
| `no_std`             | `num-format = { version = "0.4", default-features = false }`          |
| `with-num-bigint`    | `num-format = { version = "0.4", features = ["with-num-bigint"] }`    |
| `with-serde`         | `num-format = { version = "0.4", features = ["with-serde"] }`         |
| `with-system-locale` | `num-format = { version = "0.4", features = ["with-system-locale"] }` |

# License

**num-format** is licensed under either of:

- [The Apache License, Version 2.0], or
- [The MIT license]

at your option.

[bindgen]: https://crates.io/crates/bindgen
[`Buffer`]: https://docs.rs/num-format/0.4.0/num_format/struct.Buffer.html
[Common Locale Data Repository]: https://en.wikipedia.org/wiki/Common_Locale_Data_Repository
[`CustomFormat`]: https://docs.rs/num-format/0.4.0/num_format/struct.CustomFormat.html
[`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
[`fmt::Write`]: https://doc.rust-lang.org/std/fmt/fn.write.html
[`Format`]: https://docs.rs/num-format/0.4.0/num_format/trait.Format.html
[`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
[`Locale`]: https://docs.rs/num-format/0.4.0/num_format/enum.Locale.html
[`num_bigint::BigInt`]: https://docs.rs/num-bigint/0.2.2/num_bigint/struct.BigInt.html
[picking a format]: #picking-a-format
[`String`]: https://doc.rust-lang.org/std/string/struct.String.html
[The Apache License, Version 2.0]: http://www.apache.org/licenses/LICENSE-2.0
[The MIT license]: http://opensource.org/licenses/MIT
[`ToFormattedString`]: https://docs.rs/num-format/0.4.0/num_format/trait.ToFormattedString.html
[`to_formatted_string`]: https://docs.rs/num-format/0.4.0/num_format/trait.ToFormattedString.html#method.to_formatted_string
[Unicode Consortium]: https://en.wikipedia.org/wiki/Unicode_Consortium
[`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
[`WriteFormatted`]: https://docs.rs/num-format/0.4.0/num_format/trait.WriteFormatted.html
*/

#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
    dead_code,
    deprecated,
    // missing_copy_implementations,
    missing_debug_implementations,
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unused_extern_crates,
    unused_imports,
    unused_macros,
    unused_mut,
    unused_results,
    unused_parens,
    unused_unsafe,
    unused_variables
)]
#![doc(html_root_url = "https://docs.rs/num-format/0.4.0")]

#[cfg(all(feature = "with-system-locale", unix))]
#[macro_use]
extern crate cfg_if;

#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
#[macro_use]
extern crate lazy_static;

#[cfg(feature = "with-serde")]
#[macro_use]
extern crate serde;

mod buffer;
mod constants;
mod custom_format;
mod custom_format_builder;
mod error;
mod error_kind;
mod format;
mod grouping;
mod impls;
mod locale;
mod strings;
#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
mod system_locale;
mod to_formatted_str;
#[cfg(feature = "std")]
mod to_formatted_string;
#[cfg(feature = "std")]
mod write_formatted;

pub use self::buffer::Buffer;
pub use self::custom_format::CustomFormat;
pub use self::custom_format_builder::CustomFormatBuilder;
pub use self::error::Error;
pub use self::error_kind::ErrorKind;
pub use self::format::Format;
pub use self::grouping::Grouping;
pub use self::locale::Locale;
#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
pub use self::system_locale::SystemLocale;
pub use self::to_formatted_str::ToFormattedStr;
#[cfg(feature = "std")]
pub use self::to_formatted_string::ToFormattedString;
#[cfg(feature = "std")]
pub use self::write_formatted::WriteFormatted;

mod sealed {
    pub trait Sealed {}
}

pub mod utils {
    //! Utility types needed if you want to implement [`Format`] on your own type.
    //!
    //! [`Format`]: trait.Format.html

    pub use crate::strings::{
        DecimalStr, InfinityStr, MinusSignStr, NanStr, PlusSignStr, SeparatorStr,
    };
}