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