num_format/
lib.rs

1/*!
2[![Crates.io](https://img.shields.io/crates/v/num-format.svg)](https://crates.io/crates/num-format)
3[![Documentation](https://docs.rs/num-format/badge.svg)](https://docs.rs/num-format/)
4![License](https://img.shields.io/crates/l/num_format.svg)
5
6A Rust crate for producing string representations of numbers, formatted according to international
7standards, e.g.
8
9* `"1,000,000"` for US English
10* `"10,00,000"` for Indian English
11* `"1 000 000"` for French French
12
13# Creating a string representation
14
15**num-format** offers **three** principal APIs...
16
17### `ToFormattedString`
18
19The [`ToFormattedString`] trait is the simplist of the three APIs. Just call
20[`to_formatted_string`] on a type that implements it (all the integer types in the standard library
21implement it) while providing a desired format (see [picking a format] below). That said, using
22[`ToFormattedString`] will always heap allocate; so it is the slowest of the three APIs and cannot
23be used in a `no_std` environment.
24
25```rust
26# use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "std")] {
27use num_format::{Locale, ToFormattedString};
28
29fn main() {
30    let s = 1000000.to_formatted_string(&Locale::en);
31    assert_eq!(&s, "1,000,000");
32}
33# } else { fn main() {} } }
34```
35
36### `Buffer`
37
38Using the [`Buffer`] type is the fastest API, as it does **not** heap allocate. Instead, the
39formatted representation is written into a stack-allocated buffer. As such, you can use it in a
40`no_std` environment.
41
42Although this API is available for all the integer types in the standard library, it is **not**
43available for types like [`num_bigint::BigInt`] whose maximum size cannot be known in advance.
44
45```rust
46use num_format::{Buffer, Locale};
47
48fn main() {
49    // Create a stack-allocated buffer...
50    let mut buf = Buffer::default();
51
52    // Write "1,000,000" into the buffer...
53    buf.write_formatted(&1000000, &Locale::en);
54
55    // Get a view into the buffer as a &str...
56    let s = buf.as_str();
57
58    // Do what you want with the &str...
59    assert_eq!("1,000,000", s);
60}
61```
62
63### `WriteFormatted`
64
65The [`WriteFormatted`] trait is in between the other two APIs. You can write a formatted
66representation into any type that implements [`WriteFormatted`] (all the types in the standard
67library that implement [`io::Write`] or [`fmt::Write`] implement [`WriteFormatted`], such as
68[`Vec`], [`String`], [`File`], etc.).
69
70If you're writing a number type that can use the [`Buffer`] API, there is **no** heap allocation.
71That said, the [`io::Write`] and [`fmt::Write`] machinery adds a bit of overhead; so it's faster
72to use the [`Buffer`] type directly. This trait is **not** available in a `no_std` environment.
73
74```rust
75# use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "std")] {
76use num_format::{Locale, WriteFormatted};
77
78fn main() {
79    // Create a writer...
80    let mut writer = String::new(); // Could also be Vec::new(), File::open(...), ...
81
82    // Write "1,000,000" into the writer...
83    writer.write_formatted(&1000000, &Locale::en);
84
85    assert_eq!(&writer, "1,000,000");
86}
87# } else { fn main() {} } }
88```
89
90# Picking a format
91
92Formatting options (e.g. which thousands separator to use, what the minus sign looks like, etc.)
93are represented by the [`Format`] trait. This crate offers **three** concrete implementations of
94the [`Format`] trait...
95
96### `Locale`
97
98The [`Locale`] type is a programatically generated enum representing formatting standards from the
99[Common Locale Data Repository], which is maintained by the [Unicode Consortium] and used by
100Apple in macOS and iOS, by LibreOffice, by IBM in AIX, among others.
101
102```rust
103use num_format::{Grouping, Locale};
104
105fn main() {
106    let locale = Locale::en;
107    assert_eq!(locale.grouping(), Grouping::Standard);
108    assert_eq!(locale.minus_sign(), "-");
109    assert_eq!(locale.name(), "en");
110    assert_eq!(locale.separator(), ",");
111
112    let locale2 = Locale::from_name("en").unwrap();
113    assert_eq!(locale, locale2);
114
115    let available = Locale::available_names();
116    println!("All of the locale names available in the Unicode database are...");
117    println!("{:#?}", available);
118}
119```
120
121### `SystemLocale` *(available behind feature flag `with-system-locale`)*
122
123The `SystemLocale` type is another type that implements [`Format`]. It allows you to access your
124OS's locale information. It has a very similar API to [`Locale`] and should work on all major
125operating systems (i.e. macOS, linux, the BSDs, and Windows).
126
127<i>Since this type requires several dependencies (especially on Windows), it is behind a feature
128flag. To use it, include `num-format = { version = "0.4.3", features = ["with-system-locale"] }`
129in your `Cargo.toml`. Additionally, on Windows (but **only** on Windows), using `SystemLocale`
130requires Clang 3.9 or higher.</i>
131
132```rust
133# #[cfg(all(feature = "with-system-locale", any(unix, windows)))]
134use num_format::SystemLocale;
135
136# #[cfg(all(feature = "with-system-locale", any(unix, windows)))]
137fn main() {
138    let locale = SystemLocale::default().unwrap();
139    println!("My system's default locale is...");
140    println!("{:#?}", &locale);
141
142    let available = SystemLocale::available_names().unwrap();
143    println!("My available locale names are...");
144    println!("{:#?}", available);
145
146    match SystemLocale::from_name("en_US") {
147        Ok(_) => println!("My system has the 'en_US' locale."),
148        Err(_) => println!("The 'en_US' locale is not included with my system."),
149    }
150}
151# #[cfg(not(all(feature = "with-system-locale", any(unix, windows))))]
152# fn main() {}
153```
154
155### `CustomFormat`
156
157[`CustomFormat`] is the third and final type that implements [`Format`]. You can use it to build
158your own custom formats.
159
160```rust
161use num_format::{Buffer, Error, CustomFormat, Grouping};
162
163fn main() -> Result<(), Error> {
164    let format = CustomFormat::builder()
165        .grouping(Grouping::Indian)
166        .minus_sign("🙌")
167        .separator("😀")
168        .build()?;
169
170    let mut buf = Buffer::new();
171    buf.write_formatted(&(-1000000), &format);
172    assert_eq!("🙌10😀00😀000", buf.as_str());
173
174    Ok(())
175}
176```
177
178# Requirements
179
180* Rust 1.56.0 or greater if compiled with `--no-default-features`
181* Rust 1.58.0 or greater if compiled with default features
182* If you're using the `with-system-locale` feature **and** you're on Windows, Clang 3.9 or higher
183  is also required. See [here](https://rust-lang.github.io/rust-bindgen/requirements.html) for
184  installation instructions.
185
186# Extra features
187
188| Available features   | What to put in your `Cargo.toml`                                      |
189| :------------------- | :-------------------------------------------------------------------- |
190| `no_std`             | `num-format = { version = "0.4.3", default-features = false }`          |
191| `with-num-bigint`    | `num-format = { version = "0.4.3", features = ["with-num-bigint"] }`    |
192| `with-serde`         | `num-format = { version = "0.4.3", features = ["with-serde"] }`         |
193| `with-system-locale` | `num-format = { version = "0.4.3", features = ["with-system-locale"] }` |
194
195# License
196
197**num-format** is licensed under either of:
198
199- [The Apache License, Version 2.0], or
200- [The MIT license]
201
202at your option.
203
204[bindgen]: https://crates.io/crates/bindgen
205[`Buffer`]: https://docs.rs/num-format/0.4.3/num_format/struct.Buffer.html
206[Common Locale Data Repository]: https://en.wikipedia.org/wiki/Common_Locale_Data_Repository
207[`CustomFormat`]: https://docs.rs/num-format/0.4.3/num_format/struct.CustomFormat.html
208[`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
209[`fmt::Write`]: https://doc.rust-lang.org/std/fmt/fn.write.html
210[`Format`]: https://docs.rs/num-format/0.4.3/num_format/trait.Format.html
211[`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
212[`Locale`]: https://docs.rs/num-format/0.4.3/num_format/enum.Locale.html
213[`num_bigint::BigInt`]: https://docs.rs/num-bigint/0.2.2/num_bigint/struct.BigInt.html
214[picking a format]: #picking-a-format
215[`String`]: https://doc.rust-lang.org/std/string/struct.String.html
216[The Apache License, Version 2.0]: http://www.apache.org/licenses/LICENSE-2.0
217[The MIT license]: http://opensource.org/licenses/MIT
218[`ToFormattedString`]: https://docs.rs/num-format/0.4.3/num_format/trait.ToFormattedString.html
219[`to_formatted_string`]: https://docs.rs/num-format/0.4.3/num_format/trait.ToFormattedString.html#method.to_formatted_string
220[Unicode Consortium]: https://en.wikipedia.org/wiki/Unicode_Consortium
221[`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
222[`WriteFormatted`]: https://docs.rs/num-format/0.4.3/num_format/trait.WriteFormatted.html
223*/
224
225#![cfg_attr(not(feature = "std"), no_std)]
226#![allow(clippy::needless_doctest_main)]
227#![deny(
228    dead_code,
229    deprecated,
230    future_incompatible,
231    missing_copy_implementations,
232    missing_debug_implementations,
233    missing_docs,
234    nonstandard_style,
235    rust_2018_idioms,
236    trivial_casts,
237    trivial_numeric_casts,
238    unused
239)]
240#![doc(html_root_url = "https://docs.rs/num-format/0.4.4")]
241
242#[cfg(all(feature = "with-system-locale", unix))]
243#[macro_use]
244extern crate cfg_if;
245
246#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
247#[macro_use]
248extern crate lazy_static;
249
250#[cfg(feature = "with-serde")]
251#[macro_use]
252extern crate serde;
253
254mod buffer;
255mod constants;
256mod custom_format;
257mod custom_format_builder;
258mod error;
259mod error_kind;
260mod format;
261mod grouping;
262mod impls;
263mod locale;
264pub mod parsing;
265mod strings;
266#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
267mod system_locale;
268mod to_formatted_str;
269#[cfg(feature = "std")]
270mod to_formatted_string;
271#[cfg(feature = "std")]
272mod write_formatted;
273
274pub use self::buffer::Buffer;
275pub use self::custom_format::CustomFormat;
276pub use self::custom_format_builder::CustomFormatBuilder;
277pub use self::error::Error;
278pub use self::error_kind::ErrorKind;
279pub use self::format::Format;
280pub use self::grouping::Grouping;
281pub use self::locale::Locale;
282#[cfg(all(feature = "with-system-locale", any(unix, windows)))]
283pub use self::system_locale::SystemLocale;
284pub use self::to_formatted_str::ToFormattedStr;
285#[cfg(feature = "std")]
286pub use self::to_formatted_string::ToFormattedString;
287#[cfg(feature = "std")]
288pub use self::write_formatted::WriteFormatted;
289
290mod sealed {
291    pub trait Sealed {}
292}
293
294pub mod utils {
295    //! Utility types needed if you want to implement [`Format`] on your own type.
296    //!
297    //! [`Format`]: trait.Format.html
298
299    pub use crate::strings::{
300        DecimalStr, InfinityStr, MinusSignStr, NanStr, PlusSignStr, SeparatorStr,
301    };
302}