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!("1000000", 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