# 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 num_format::{Locale, ToFormattedString};

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

#### `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 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");
}
```

## 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).

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

```rust
use num_format::SystemLocale;

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."),
    }
}
```

#### `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.56.0 or greater if compiled with `--no-default-features`
* Rust 1.58.0 or greater if compiled with default features
* 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.3", default-features = false }`          |
| `with-num-bigint`    | `num-format = { version = "0.4.3", features = ["with-num-bigint"] }`    |
| `with-serde`         | `num-format = { version = "0.4.3", features = ["with-serde"] }`         |
| `with-system-locale` | `num-format = { version = "0.4.3", 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.3/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.3/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.3/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.3/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.3/num_format/trait.ToFormattedString.html
[`to_formatted_string`]: https://docs.rs/num-format/0.4.3/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.3/num_format/trait.WriteFormatted.html
