diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml new file mode 100644 index 00000000..6b85d4ed --- /dev/null +++ b/.github/workflows/Docs.yml @@ -0,0 +1,27 @@ +name: Docs + +on: + [workflow_dispatch] + +jobs: + docs: + name: Test Docs Combinations + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install latest nightly + uses: dtolnay/rust-toolchain@nightly + - run: cargo --version + - run: python --version + - run: cargo check + - run: cargo build + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --features=format + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --features=radix + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --features=format,radix + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --features=write-integers,write-floats + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --features=parse-integers,parse-floats + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-default-features --all-features + # validate all the generated docs.rs docs + - run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --features=format,radix + - run: python scripts/docs.py diff --git a/CHANGELOG b/CHANGELOG index 9b4eee73..ff42ffb1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,10 +10,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Additional trait impls for `f16` and `bf16` to better match Rust's interface. +- Added `Options::buffer_size_const` for integer and float writers. +- Added `build_checked` and `build_unchecked` to our `NumberFormatBuilder` API. +- Added `build_checked` to our `Options` API. +- Added `has_digit_separator` to `NumberFormat`. +- Re-export `NumberFormat` to our other crates. ### Changed -- Lowered the MSRV from 1.63.0 to 1.61.0 and adds support for most testing on 1.61.0. +- Lowered the MSRV from 1.63.0 to 1.61.0 and adds support for most testing on 1.60.0. +- Reduced the required buffer size for integer and float writers when using `buffer_size` and `buffer_size_const` for decimal numbers. +- Deprecated `NumberFormatBuilder::build` due to a lack of validation. +- Deprecated `Options::set_*` in our write float API since options should be considered immutable. +- Removed `static_assertions` dependency. + +### Fixed + +- Bug where the `radix` feature wasn't enabling `power-of-two` in `lexical-core` or `lexical`. ## [1.0.5] 2024-12-08 diff --git a/README.md b/README.md index 9703344a..275bfd22 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ where let value: T = lexical_core::parse(value.as_bytes())?; let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; let bytes = lexical_core::write(value * multiplier, &mut buffer); - Ok(std::str::from_utf8(bytes).unwrap()) + Ok(String::from_utf8(bytes).unwrap()) } ``` @@ -93,7 +93,9 @@ let (x, count): (i32, usize) = lexical_core::parse_partial(b"123 456")?; ## no_std -`lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a `no_std` environment, add the following to `Cargo.toml`: +`lexical-core` does not depend on a standard library, nor a system allocator. To use `lexical-core` in a [`no_std`] environment, add the following to `Cargo.toml`: + +[`no_std`]: ```toml [dependencies.lexical-core] @@ -103,7 +105,7 @@ default-features = false features = ["write-integers", "write-floats", "parse-integers", "parse-floats"] ``` -And get started using lexical: +And get started using `lexical-core`: ```rust // A constant for the maximum number of bytes a formatter will write. @@ -136,9 +138,9 @@ Lexical is highly customizable, and contains numerous other optional features: - **std**:   Enable use of the Rust standard library (enabled by default). - **power-of-two**:   Enable conversions to and from non-decimal strings. -
With power_of_two enabled, the radixes {2, 4, 8, 10, 16, and 32} are valid, otherwise, only 10 is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.
+
With power_of_two enabled, the radixes {2, 4, 8, 10, 16, and 32} are valid, otherwise, only 10 is valid. This enables common conversions to/from hexadecimal integers/floats, without requiring large pre-computed tables for other radixes.
- **radix**:   Allow conversions to and from non-decimal strings. -
With radix enabled, any radix from 2 to 36 (inclusive) is valid, otherwise, only 10 is valid.
+
With radix enabled, any radix from 2 to 36 (inclusive) is valid, otherwise, only 10 is valid.
- **format**:   Customize acceptable number formats for number parsing and writing.
With format enabled, the number format is dictated through bitflags and masks packed into a u128. These dictate the valid syntax of parsed and written numbers, including enabling digit separators, requiring integer or fraction digits, and toggling case-sensitive exponent characters.
- **compact**:   Optimize for binary size at the expense of performance. @@ -154,7 +156,7 @@ Lexical also places a heavy focus on code bloat: with algorithms both optimized Lexical is extensively customizable to support parsing numbers from a wide variety of programming languages, such as `1_2_3`. However, lexical takes the concept of "you don't pay for what you don't use" seriously: enabling the `format` feature does not affect the performance of parsing regular numbers: only those with digit separators. -> ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, lexical-core will panic. +> ⚠ **WARNING:** When changing the number of significant digits written, disabling the use of exponent notation, or changing exponent notation thresholds, `BUFFER_SIZE` may be insufficient to hold the resulting output. `WriteOptions::buffer_size_const` will provide a correct upper bound on the number of bytes written. If a buffer of insufficient length is provided, `lexical-core` will panic. Every language has competing specifications for valid numerical input, meaning a number parser for Rust will incorrectly accept or reject input for different programming or data languages. For example: @@ -165,9 +167,9 @@ let f: f64 = lexical_core::parse(b"3.e7")?; // 3e7 // Let's only accept JSON floats. const JSON: u128 = lexical_core::format::JSON; -let options = ParseFloatOptions::new(); -let f: f64 = lexical_core::parse_with_options::(b"3.0e7", &options)?; // 3e7 -let f: f64 = lexical_core::parse_with_options::(b"3.e7", &options)?; // Errors! +const OPTIONS: ParseFloatOptions = ParseFloatOptions::new(); +let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.0e7", &OPTIONS)?; // 3e7 +let f: f64 = lexical_core::parse_with_options::<_, JSON>(b"3.e7", &OPTIONS)?; // Errors! ``` Due the high variability in the syntax of numbers in different programming and data languages, we provide 2 different APIs to simplify converting numbers with different syntax requirements. @@ -196,24 +198,30 @@ When the `format` feature is enabled, numerous other syntax and digit separator Many pre-defined constants therefore exist to simplify common use-cases, including: - -- JSON, XML, TOML, YAML, SQLite, and many more. -- Rust, Python, C#, FORTRAN, COBOL literals and strings, and many more. +- [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +- [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, and many more. + +[`JSON`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.COBOL_LITERAL.html An example of building a custom number format is as follows: ```rust +// this will panic if the format is invalid const FORMAT: u128 = lexical_core::NumberFormatBuilder::new() // Disable exponent notation. .no_exponent_notation(true) // Disable all special numbers, such as Nan and Inf. .no_special(true) - .build(); - -// Due to use in a `const fn`, we can't panic or expect users to unwrap invalid -// formats, so it's up to the caller to verify the format. If an invalid format -// is provided to a parser or writer, the function will error or panic, respectively. -debug_assert!(lexical_core::format_is_valid::()); + .build_strict(); ``` ### Options API @@ -225,7 +233,7 @@ An example of building a custom options struct is as follows: ```rust use std::num; -let options = lexical_core::WriteFloatOptions::builder() +const OPTIONS: lexical_core::WriteFloatOptions = lexical_core::WriteFloatOptions::builder() // Only write up to 5 significant digits, IE, `1.23456` becomes `1.2345`. .max_significant_digits(num::NonZeroUsize::new(5)) // Never write less than 5 significant digits, `1.1` becomes `1.1000`. @@ -238,8 +246,7 @@ let options = lexical_core::WriteFloatOptions::builder() .nan_string(None) // Write infinity as "Infinity". .inf_string(Some(b"Infinity")) - .build() - .unwrap(); + .build_strict(); ``` ## Documentation @@ -380,17 +387,17 @@ lexical-core should also work on a wide variety of other architectures and ISAs. The currently supported versions are: - v1.0.x -Due to security considerations, all other versions are not supported and security advisories exist for them.. +Due to security considerations, all other versions are not supported and security advisories exist for them. **Rustc Compatibility** - v1.0.x supports 1.63+, including stable, beta, and nightly. -Please report any errors compiling a supported lexical-core version on a compatible Rustc version. +Please report any errors compiling a supported `lexical` version on a compatible Rustc version. **Versioning** -lexical uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change. +`lexical` uses [semantic versioning](https://semver.org/). Removing support for Rustc versions newer than the latest stable Debian or Ubuntu version is considered an incompatible API change, requiring a major version change. ## Changelog @@ -402,6 +409,6 @@ Lexical is dual licensed under the Apache 2.0 license as well as the MIT license ## Contributing -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in lexical by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md). +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in `lexical` by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Contributing to the repository means abiding by the [code of conduct](https://github.com/Alexhuszagh/rust-lexical/blob/main/CODE_OF_CONDUCT.md). -For the process on how to contribute to lexical, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide. +For the process on how to contribute to `lexical`, see the [development](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/Development.md) quick-start guide. diff --git a/extras/parse-integer/tests/api_tests.rs b/extras/parse-integer/tests/api_tests.rs index e51e571d..13abb2ca 100644 --- a/extras/parse-integer/tests/api_tests.rs +++ b/extras/parse-integer/tests/api_tests.rs @@ -58,14 +58,14 @@ proptest! { #[test] #[cfg(feature = "power-of-two")] fn i32_binary_roundtrip_display_proptest(i in i32::MIN..i32::MAX) { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = from_radix(2); let digits = if i < 0 { format!("-{:b}", (i as i64).wrapping_neg()) } else { format!("{:b}", i) }; - let result = i32::from_lexical_with_options::(digits.as_bytes(), &options); + let result = i32::from_lexical_with_options::(digits.as_bytes(), &OPTIONS); prop_assert_eq!(i, result.unwrap()); } diff --git a/extras/write-float/tests/algorithm_tests.rs b/extras/write-float/tests/algorithm_tests.rs index f89b151a..1aa24b28 100644 --- a/extras/write-float/tests/algorithm_tests.rs +++ b/extras/write-float/tests/algorithm_tests.rs @@ -15,12 +15,12 @@ const DECIMAL: u128 = NumberFormatBuilder::decimal(); default_quickcheck! { fn f32_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if f.is_special() { true } else { - let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); roundtrip == Ok(f) @@ -29,12 +29,12 @@ default_quickcheck! { fn f64_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if f.is_special() { true } else { - let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); roundtrip == Ok(f) @@ -48,10 +48,10 @@ proptest! { #[test] fn f32_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if !f.is_special() { - let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); prop_assert_eq!(roundtrip, Ok(f)) @@ -61,10 +61,10 @@ proptest! { #[test] fn f64_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if !f.is_special() { - let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); prop_assert_eq!(roundtrip, Ok(f)) diff --git a/extras/write-float/tests/binary_tests.rs b/extras/write-float/tests/binary_tests.rs index efcf6915..ae46757c 100644 --- a/extras/write-float/tests/binary_tests.rs +++ b/extras/write-float/tests/binary_tests.rs @@ -18,12 +18,12 @@ const OCTAL: u128 = NumberFormatBuilder::octal(); default_quickcheck! { fn f32_binary_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if f.is_special() { true } else { let f = f.abs(); - let count = binary::write_float::<_, BINARY>(f, &mut buffer, &options); + let count = binary::write_float::<_, BINARY>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 2, b'e'); roundtrip == f } @@ -31,12 +31,12 @@ default_quickcheck! { fn f32_octal_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if f.is_special() { true } else { let f = f.abs(); - let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &options); + let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 8, b'e'); roundtrip == f } @@ -44,12 +44,12 @@ default_quickcheck! { fn f64_binary_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if f.is_special() { true } else { let f = f.abs(); - let count = binary::write_float::<_, BINARY>(f, &mut buffer, &options); + let count = binary::write_float::<_, BINARY>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 2, b'e'); roundtrip == f } @@ -57,12 +57,12 @@ default_quickcheck! { fn f64_octal_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if f.is_special() { true } else { let f = f.abs(); - let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &options); + let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 8, b'e'); roundtrip == f } @@ -75,10 +75,10 @@ proptest! { #[test] fn f32_binary_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !f.is_special() { let f = f.abs(); - let count = binary::write_float::<_, BINARY>(f, &mut buffer, &options); + let count = binary::write_float::<_, BINARY>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 2, b'e'); prop_assert_eq!(roundtrip, f) } @@ -87,10 +87,10 @@ proptest! { #[test] fn f32_octal_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !f.is_special() { let f = f.abs(); - let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &options); + let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 8, b'e'); prop_assert_eq!(roundtrip, f) } @@ -99,10 +99,10 @@ proptest! { #[test] fn f64_binary_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !f.is_special() { let f = f.abs(); - let count = binary::write_float::<_, BINARY>(f, &mut buffer, &options); + let count = binary::write_float::<_, BINARY>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 2, b'e'); prop_assert_eq!(roundtrip, f) } @@ -111,10 +111,10 @@ proptest! { #[test] fn f64_octal_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !f.is_special() { let f = f.abs(); - let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &options); + let count = binary::write_float::<_, OCTAL>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 8, b'e'); prop_assert_eq!(roundtrip, f) } diff --git a/extras/write-float/tests/compact_tests.rs b/extras/write-float/tests/compact_tests.rs index aee524e6..1cadf8d9 100644 --- a/extras/write-float/tests/compact_tests.rs +++ b/extras/write-float/tests/compact_tests.rs @@ -15,12 +15,12 @@ const DECIMAL: u128 = NumberFormatBuilder::decimal(); default_quickcheck! { fn f32_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if f.is_special() { true } else { - let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); roundtrip == Ok(f) @@ -29,12 +29,12 @@ default_quickcheck! { fn f64_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if f.is_special() { true } else { - let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); roundtrip == Ok(f) @@ -48,10 +48,10 @@ proptest! { #[test] fn f32_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if !f.is_special() { - let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); prop_assert_eq!(roundtrip, Ok(f)) @@ -61,10 +61,10 @@ proptest! { #[test] fn f64_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); let f = f.abs(); if !f.is_special() { - let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(f, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); prop_assert_eq!(roundtrip, Ok(f)) diff --git a/extras/write-float/tests/radix_tests.rs b/extras/write-float/tests/radix_tests.rs index 98adbc8f..3d805964 100644 --- a/extras/write-float/tests/radix_tests.rs +++ b/extras/write-float/tests/radix_tests.rs @@ -103,148 +103,138 @@ where #[test] fn write_float_test() { // Check no formatting, binary, and when exponent notation is used. - let options = Options::builder().build().unwrap(); - write_float::<_, BASE3>(0.0f64, &options, "0.0"); - write_float::<_, BASE3>(1.0f64, &options, "1.0"); - write_float::<_, BASE3>(2.0f64, &options, "2.0"); - write_float::<_, BASE3>(0.49999999999f64, &options, "0.111111111111111111111101200020121"); - write_float::<_, BASE3>(0.5f64, &options, "0.1111111111111111111111111111111112"); - write_float::<_, BASE3>(0.75f64, &options, "0.202020202020202020202020202020202"); - write_float::<_, BASE3>(0.9998475842097241f64, &options, "0.22222222"); + const OPTS1: Options = Options::builder().build_strict(); + write_float::<_, BASE3>(0.0f64, &OPTS1, "0.0"); + write_float::<_, BASE3>(1.0f64, &OPTS1, "1.0"); + write_float::<_, BASE3>(2.0f64, &OPTS1, "2.0"); + write_float::<_, BASE3>(0.49999999999f64, &OPTS1, "0.111111111111111111111101200020121"); + write_float::<_, BASE3>(0.5f64, &OPTS1, "0.1111111111111111111111111111111112"); + write_float::<_, BASE3>(0.75f64, &OPTS1, "0.202020202020202020202020202020202"); + write_float::<_, BASE3>(0.9998475842097241f64, &OPTS1, "0.22222222"); // Adapted from bugs in quickcheck. write_float::<_, BASE3>( 1.7976931348623157e+308f64, - &options, + &OPTS1, "1.0020200012020012100112000100111212e212221", ); // Adapted from bugs in quickcheck. - write_float::<_, BASE3>(3.4028235e+38f32, &options, "2.022011021210002e2222"); + write_float::<_, BASE3>(3.4028235e+38f32, &OPTS1, "2.022011021210002e2222"); // Try changing the exponent limits. - let options = Options::builder() + const OPTS2: Options = Options::builder() .negative_exponent_break(num::NonZeroI32::new(-6)) .positive_exponent_break(num::NonZeroI32::new(10)) - .build() - .unwrap(); - write_float::<_, BASE3>(1501.2344967901236f64, &options, "2001121.02002222112101212200212222"); - write_float::<_, BASE3>( - 0.02290702051986883f64, - &options, - "0.000121200212201201002110120212011", - ); - write_float::<_, BASE3>(10e9f64, &options, "2.21210220202122010101e202"); + .build_strict(); + write_float::<_, BASE3>(1501.2344967901236f64, &OPTS2, "2001121.02002222112101212200212222"); + write_float::<_, BASE3>(0.02290702051986883f64, &OPTS2, "0.000121200212201201002110120212011"); + write_float::<_, BASE3>(10e9f64, &OPTS2, "2.21210220202122010101e202"); // Check max digits. - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float::<_, BASE3>(0.0f64, &options, "0.0"); - write_float::<_, BASE3>(1.0f64, &options, "1.0"); - write_float::<_, BASE3>(2.0f64, &options, "2.0"); - write_float::<_, BASE3>(0.49999999999f64, &options, "0.11111"); - write_float::<_, BASE3>(0.5f64, &options, "0.11112"); - write_float::<_, BASE3>(0.75f64, &options, "0.20202"); - write_float::<_, BASE3>(0.9998475842097241f64, &options, "1.0"); + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float::<_, BASE3>(0.0f64, &OPTS3, "0.0"); + write_float::<_, BASE3>(1.0f64, &OPTS3, "1.0"); + write_float::<_, BASE3>(2.0f64, &OPTS3, "2.0"); + write_float::<_, BASE3>(0.49999999999f64, &OPTS3, "0.11111"); + write_float::<_, BASE3>(0.5f64, &OPTS3, "0.11112"); + write_float::<_, BASE3>(0.75f64, &OPTS3, "0.20202"); + write_float::<_, BASE3>(0.9998475842097241f64, &OPTS3, "1.0"); // Check min digits. - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float::<_, BASE3>(0.0f64, &options, "0.0000"); - write_float::<_, BASE3>(1.0f64, &options, "1.0000"); - write_float::<_, BASE3>(2.0f64, &options, "2.0000"); - write_float::<_, BASE3>(0.49999999999f64, &options, "0.111111111111111111111101200020121"); + const OPTS4: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float::<_, BASE3>(0.0f64, &OPTS4, "0.0000"); + write_float::<_, BASE3>(1.0f64, &OPTS4, "1.0000"); + write_float::<_, BASE3>(2.0f64, &OPTS4, "2.0000"); + write_float::<_, BASE3>(0.49999999999f64, &OPTS4, "0.111111111111111111111101200020121"); // Check max digits and trim floats. - let options = Options::builder() + const OPTS5: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, BASE3>(0.2345678901234567890f64, &options, "0.0201"); - write_float::<_, BASE3>(23.45678901234567890f64, &options, "212.11"); - write_float::<_, BASE3>(93.82715604938272f64, &options, "10111"); - write_float::<_, BASE3>(375.3086241975309f64, &options, "111220"); + .build_strict(); + write_float::<_, BASE3>(0.2345678901234567890f64, &OPTS5, "0.0201"); + write_float::<_, BASE3>(23.45678901234567890f64, &OPTS5, "212.11"); + write_float::<_, BASE3>(93.82715604938272f64, &OPTS5, "10111"); + write_float::<_, BASE3>(375.3086241975309f64, &OPTS5, "111220"); // Check min digits and trim floats. - let options = Options::builder() + const OPTS6: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(50)) .trim_floats(true) - .build() - .unwrap(); + .build_strict(); write_float::<_, BASE3>( 2.9999999999999f64, - &options, + &OPTS6, "2.2222222222222222222222222220201100000000000000000", ); - write_float::<_, BASE3>(3.0f64, &options, "10"); + write_float::<_, BASE3>(3.0f64, &OPTS6, "10"); write_float::<_, BASE3>( 8.9999999999999f64, - &options, + &OPTS6, "22.222222222222222222222222222020200000000000000000", ); - write_float::<_, BASE3>(9.0f64, &options, "100"); + write_float::<_, BASE3>(9.0f64, &OPTS6, "100"); write_float::<_, BASE3>( 0.33333333f64, - &options, + &OPTS6, "0.0222222222222222212010101201000200000000000000000", ); write_float::<_, BASE3>( 12157665459056928801.0f64, - &options, + &OPTS6, "2.2222222222222222222222222222222220000000000000000e1110", ); write_float::<_, BASE3>( 8.225263339969959e-20f64, - &options, + &OPTS6, "2.2222222222222222222222222222222020000000000000000e-1112", ); // Check carry. - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(3)).build().unwrap(); - write_float::<_, BASE3>(2.9999999999999f64, &options, "10.0"); - write_float::<_, BASE3>(3.0f64, &options, "10.0"); - write_float::<_, BASE3>(8.9999999999999f64, &options, "100.0"); - write_float::<_, BASE3>(9.0f64, &options, "100.0"); - write_float::<_, BASE3>(12157665459056928801.0f64, &options, "1.0e1111"); - write_float::<_, BASE3>(8.225263339969959e-20f64, &options, "1.0e-1111"); + const OPTS7: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(3)).build_strict(); + write_float::<_, BASE3>(2.9999999999999f64, &OPTS7, "10.0"); + write_float::<_, BASE3>(3.0f64, &OPTS7, "10.0"); + write_float::<_, BASE3>(8.9999999999999f64, &OPTS7, "100.0"); + write_float::<_, BASE3>(9.0f64, &OPTS7, "100.0"); + write_float::<_, BASE3>(12157665459056928801.0f64, &OPTS7, "1.0e1111"); + write_float::<_, BASE3>(8.225263339969959e-20f64, &OPTS7, "1.0e-1111"); // Check carry and trim floats. - let options = Options::builder() + const OPTS8: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(3)) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, BASE3>(3.0f64, &options, "10"); - write_float::<_, BASE3>(9.0f64, &options, "100"); - write_float::<_, BASE3>(12157665459056928801.0f64, &options, "1e1111"); - write_float::<_, BASE3>(8.225263339969959e-20f64, &options, "1e-1111"); + .build_strict(); + write_float::<_, BASE3>(3.0f64, &OPTS8, "10"); + write_float::<_, BASE3>(9.0f64, &OPTS8, "100"); + write_float::<_, BASE3>(12157665459056928801.0f64, &OPTS8, "1e1111"); + write_float::<_, BASE3>(8.225263339969959e-20f64, &OPTS8, "1e-1111"); // Test the round mode. - let truncate = Options::builder() + const TRUNCATE: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(2)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(2)) .round_mode(RoundMode::Round) - .build() - .unwrap(); - write_float::<_, BASE3>(23.45678901234567890f64, &round, "220.0"); - write_float::<_, BASE3>(23.45678901234567890f64, &truncate, "210.0"); + .build_strict(); + write_float::<_, BASE3>(23.45678901234567890f64, &ROUND, "220.0"); + write_float::<_, BASE3>(23.45678901234567890f64, &TRUNCATE, "210.0"); } macro_rules! test_radix { ($parse:ident, $f:ident, $radix:expr, $buffer:ident, $options:ident) => {{ const FORMAT: u128 = NumberFormatBuilder::from_radix($radix); - let options = if $radix >= 15 { - $options.rebuild().exponent(b'^').build().unwrap() + const OPTS: Options = if $radix >= 15 { + $options.rebuild().exponent(b'^').build_strict() } else { - $options.clone() + $options.rebuild().build_strict() }; - let count = radix::write_float::<_, FORMAT>($f, &mut $buffer, &options); - let roundtrip = $parse(&$buffer[..count], $radix, options.exponent()); + let count = radix::write_float::<_, FORMAT>($f, &mut $buffer, &OPTS); + let roundtrip = $parse(&$buffer[..count], $radix, OPTS.exponent()); assert_relative_eq!($f, roundtrip, epsilon = 1e-6, max_relative = 3e-6); }}; } @@ -286,62 +276,60 @@ macro_rules! test_all { #[test] fn f32_radix_roundtrip_test() { let mut buffer = [b'\x00'; 1200]; - let options = Options::new(); + const OPTIONS: Options = Options::new(); for &f in F32_DATA.iter() { - test_all!(parse_f32, f, buffer, options); + test_all!(parse_f32, f, buffer, OPTIONS); } } #[test] fn f64_radix_roundtrip_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::new(); + const OPTIONS: Options = Options::new(); for &f in F64_DATA.iter() { - test_all!(parse_f64, f, buffer, options); + test_all!(parse_f64, f, buffer, OPTIONS); } } #[test] fn base21_test() { let mut buffer = [b'\x00'; 512]; - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTS1: Options = Options::builder().exponent(b'^').build_strict(); let f = 2879632400000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS1); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); assert_relative_eq!(f, roundtrip, epsilon = 1e-5, max_relative = 1e-5); let f = 48205284000000000000000000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS1); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); assert_relative_eq!(f, roundtrip, epsilon = 1e-5, max_relative = 1e-5); - let options = Options::builder() + const OPTS2: Options = Options::builder() .exponent(b'^') .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); let f = 105861640000000000000000000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS2); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); assert_relative_eq!(f, roundtrip, epsilon = 1e-1, max_relative = 1e-1); let f = 63900220000000000000000000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS2); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); assert_relative_eq!(f, roundtrip, epsilon = 1e-1, max_relative = 1e-1); let f = 48205284000000000000000000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS2); assert_eq!(b"4.C44^17", &buffer[..count]); - let options = Options::builder() + const OPTS3: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(0x1000)) .negative_exponent_break(num::NonZeroI32::new(-0x1000)) - .build() - .unwrap(); + .build_strict(); let f = 48205284000000000000000000000000000000.0f32; - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTS3); assert_eq!(b"4C440700000000000000000000000.0", &buffer[..count]); } @@ -365,12 +353,12 @@ macro_rules! is_overflow { default_quickcheck! { fn f32_base3_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if is_overflow!(@f32 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6) } @@ -378,12 +366,12 @@ default_quickcheck! { fn f32_base5_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if is_overflow!(@f32 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6) } @@ -391,12 +379,12 @@ default_quickcheck! { fn f32_base21_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); if is_overflow!(@f32 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); relative_eq!(f, roundtrip, epsilon=1e-5, max_relative=1e-5) } @@ -404,12 +392,12 @@ default_quickcheck! { fn f64_base3_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if is_overflow!(@f64 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6) } @@ -417,12 +405,12 @@ default_quickcheck! { fn f64_base5_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if is_overflow!(@f64 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6) } @@ -430,12 +418,12 @@ default_quickcheck! { fn f64_base21_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); if is_overflow!(@f64 f) { true } else { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6) } @@ -448,10 +436,10 @@ proptest! { #[test] fn f32_base3_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6); prop_assert!(equal) @@ -461,10 +449,10 @@ proptest! { #[test] fn f32_base5_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6); prop_assert!(equal) @@ -474,10 +462,10 @@ proptest! { #[test] fn f32_base21_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-5, max_relative=1e-5); prop_assert!(equal) @@ -487,13 +475,12 @@ proptest! { #[test] fn f32_base3_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -503,13 +490,12 @@ proptest! { #[test] fn f32_base5_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -519,14 +505,13 @@ proptest! { #[test] fn f32_base21_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -536,13 +521,12 @@ proptest! { #[test] fn f32_base3_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -552,13 +536,12 @@ proptest! { #[test] fn f32_base5_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -568,14 +551,13 @@ proptest! { #[test] fn f32_base21_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -585,15 +567,14 @@ proptest! { #[test] fn f32_base3_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -603,15 +584,14 @@ proptest! { #[test] fn f32_base5_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -621,16 +601,15 @@ proptest! { #[test] fn f32_base21_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -640,15 +619,14 @@ proptest! { #[test] fn f32_base3_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -658,15 +636,14 @@ proptest! { #[test] fn f32_base5_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -676,16 +653,15 @@ proptest! { #[test] fn f32_base21_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f32 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f32(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -695,10 +671,10 @@ proptest! { #[test] fn f64_base3_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6); prop_assert!(equal) @@ -708,10 +684,10 @@ proptest! { #[test] fn f64_base5_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6); prop_assert!(equal) @@ -721,10 +697,10 @@ proptest! { #[test] fn f64_base21_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-6, max_relative=1e-6); prop_assert!(equal) @@ -734,13 +710,12 @@ proptest! { #[test] fn f64_base3_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -750,13 +725,12 @@ proptest! { #[test] fn f64_base5_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -766,14 +740,13 @@ proptest! { #[test] fn f64_base21_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .max_significant_digits(num::NonZeroUsize::new(4)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -783,13 +756,12 @@ proptest! { #[test] fn f64_base3_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -799,13 +771,12 @@ proptest! { #[test] fn f64_base5_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -815,14 +786,13 @@ proptest! { #[test] fn f64_base21_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .min_significant_digits(num::NonZeroUsize::new(15)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -832,15 +802,14 @@ proptest! { #[test] fn f64_base3_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -850,15 +819,14 @@ proptest! { #[test] fn f64_base5_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -868,16 +836,15 @@ proptest! { #[test] fn f64_base21_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .max_significant_digits(num::NonZeroUsize::new(4)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -887,15 +854,14 @@ proptest! { #[test] fn f64_base3_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE3>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE3>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 3, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -905,15 +871,14 @@ proptest! { #[test] fn f64_base5_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE5>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE5>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 5, b'e'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) @@ -923,16 +888,15 @@ proptest! { #[test] fn f64_base21_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; - let options = Options::builder() + const OPTIONS: Options = Options::builder() .exponent(b'^') .min_significant_digits(num::NonZeroUsize::new(15)) .positive_exponent_break(num::NonZeroI32::new(1)) .negative_exponent_break(num::NonZeroI32::new(-1)) - .build() - .unwrap(); + .build_strict(); if !(is_overflow!(@f64 f)) { let f = f.abs(); - let count = radix::write_float::<_, BASE21>(f, &mut buffer, &options); + let count = radix::write_float::<_, BASE21>(f, &mut buffer, &OPTIONS); let roundtrip = parse_f64(&buffer[..count], 21, b'^'); let equal = relative_eq!(f, roundtrip, epsilon=1e-1, max_relative=1e-1); prop_assert!(equal) diff --git a/lexical-core/Cargo.toml b/lexical-core/Cargo.toml index 9ea76274..d6236f0a 100644 --- a/lexical-core/Cargo.toml +++ b/lexical-core/Cargo.toml @@ -10,7 +10,7 @@ name = "lexical-core" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "1.0.5" -rust-version = "1.61.0" +rust-version = "1.60.0" exclude = [ "assets/*", "docs/*", @@ -77,6 +77,7 @@ power-of-two = [ ] # Add support for parsing non-decimal float strings. radix = [ + "power-of-two", "lexical-util/radix", "lexical-write-integer?/radix", "lexical-write-float?/radix", @@ -142,3 +143,4 @@ f128 = [ [package.metadata.docs.rs] features = ["radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-core/src/lib.rs b/lexical-core/src/lib.rs index 7cf67103..ee4b4139 100644 --- a/lexical-core/src/lib.rs +++ b/lexical-core/src/lib.rs @@ -1,136 +1,314 @@ -//! Fast lexical conversion routines for a `no_std` environment. +//! Fast lexical conversion routines for a [`no_std`] environment. //! -//! lexical-core is a low-level API for number-to-string and +//! `lexical-core` is a high-performance library for number-to-string and //! string-to-number conversions, without requiring a system -//! allocator. If you would like to use a high-level API that -//! writes to and parses from `String` and `&str`, respectively, -//! please look at [lexical](https://crates.io/crates/lexical) -//! instead. +//! allocator. If you would like to use a library that writes to [`String`], +//! look at [lexical](https://crates.io/crates/lexical) instead. In addition +//! to high performance, it's also highly configurable, supporting nearly +//! every float and integer format available. //! -//! Despite the low-level API and focus on performance, lexical-core -//! strives to be simple and yet configurable: despite supporting nearly -//! every float and integer format available, it only exports 4 write -//! functions and 4 parse functions. -//! -//! lexical-core is well-tested, and has been downloaded more than 5 million -//! times and currently has no known errors in correctness. lexical-core -//! prioritizes performance above all else, and aims to be competitive -//! or faster than any other float or integer parser and writer. +//! `lexical-core` is well-tested, and has been downloaded more than 50 million +//! times and currently has no known errors in correctness. `lexical-core` +//! prioritizes performance above all else, and is competitive or faster +//! than any other float or integer parser and writer. //! //! In addition, despite having a large number of features, configurability, -//! and a focus on performance, we also strive for fast compile times. -//! Recent versions also add support for smaller binary sizes, as well -//! ideal for embedded or web environments, where executable bloat can +//! and a focus on performance, it also aims to have fast compile times. +//! Recent versions also add [`support`](#compact) for smaller binary sizes, as +//! well ideal for embedded or web environments, where executable bloat can //! be much more detrimental than performance. //! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! //! # Getting Started //! -//! ```rust -//! # #[cfg(all( -//! # feature = "parse-floats", -//! # feature = "parse-integers", -//! # feature = "write-floats", -//! # feature = "write-integers", -//! # ))] -//! # { +//! #### Parse API //! +//! The main parsing API is [`parse`] and [`parse_partial`]. For example, +//! to parse a number from bytes, validating the entire input is a number: +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "parse-integers"))] { //! // String to number using Rust slices. //! // The argument is the byte string parsed. //! let f: f32 = lexical_core::parse(b"3.5").unwrap(); // 3.5 //! let i: i32 = lexical_core::parse(b"15").unwrap(); // 15 +//! # } +//! ``` +//! +//! All `lexical-core` parsers are validating, they check the that entire +//! input data is correct, and stop parsing when invalid data is found, +//! numerical overflow, or other errors: //! -//! // All lexical_core parsers are checked, they validate the -//! // input data is entirely correct, and stop parsing when invalid data -//! // is found, or upon numerical overflow. +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "parse-integers"))] { //! let r = lexical_core::parse::(b"256"); // Err(ErrorCode::Overflow.into()) //! let r = lexical_core::parse::(b"1a5"); // Err(ErrorCode::InvalidDigit.into()) +//! # } +//! ``` //! -//! // In order to extract and parse a number from a substring of the input -//! // data, use `parse_partial`. These functions return the parsed value and -//! // the number of processed digits, allowing you to extract and parse the -//! // number in a single pass. +//! For streaming APIs or those incrementally parsing data fed to a parser, +//! where the input data is known to be a float but where the float ends is +//! currently unknown, the partial parsers will both return the data it was +//! able to parse and the number of bytes processed: +//! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { //! let r = lexical_core::parse_partial::(b"3a5"); // Ok((3, 1)) +//! # } +//! ``` //! -//! // If an insufficiently long buffer is passed, the serializer will panic. +//! #### Write API //! -//! // PANICS -//! let mut buf = [b'0'; 1]; -//! //let slc = lexical_core::write::(15, &mut buf); +//! The main parsing API is [`write`]. For example, to write a number to an +//! existing buffer: //! -//! // In order to guarantee the buffer is long enough, always ensure there -//! // are at least `T::FORMATTED_SIZE` bytes, which requires the -//! // `lexical_core::FormattedSize` trait to be in scope. +//! ```rust +//! # #[cfg(feature = "write-floats")] { //! use lexical_core::FormattedSize; +//! //! let mut buf = [b'0'; f64::FORMATTED_SIZE]; //! let slc = lexical_core::write::(15.1, &mut buf); //! assert_eq!(slc, b"15.1"); +//! # } +//! ``` //! -//! // When the `radix` feature is enabled, for decimal floats, using -//! // `T::FORMATTED_SIZE` may significantly overestimate the space -//! // required to format the number. Therefore, the -//! // `T::FORMATTED_SIZE_DECIMAL` constants allow you to get a much -//! // tighter bound on the space required. -//! let mut buf = [b'0'; f64::FORMATTED_SIZE_DECIMAL]; +//! If a buffer of an insufficient size is provided, the writer will panic: +//! +//! ```should_panic +//! # #[cfg(feature = "write-integers")] { +//! let mut buf = [b'0'; 1]; +//! let digits = lexical_core::write::(15, &mut buf); +//! # } +//! # #[cfg(not(feature = "write-integers"))] { +//! # panic!("hidden, for the doctest to pass if the feature isn't enabled."); +//! # } +//! ``` +//! +//! In order to guarantee the buffer is large enough, always ensure there +//! are at least [`T::FORMATTED_SIZE_DECIMAL`] bytes, which requires the +//! [`FormattedSize`] trait to be in scope. +//! +//! +#![cfg_attr( + feature = "write", + doc = " +[`FormattedSize`]: FormattedSize +[`T::FORMATTED_SIZE_DECIMAL`]: FormattedSize::FORMATTED_SIZE_DECIMAL +" +)] +#![cfg_attr( + not(feature = "write"), + doc = " +[`FormattedSize`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FormattedSize.html +[`T::FORMATTED_SIZE_DECIMAL`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FormattedSize.html#associatedconstant.FORMATTED_SIZE_DECIMAL +" +)] +//! +//! ```rust +//! # #[cfg(feature = "write-integers")] { +//! use lexical_core::FormattedSize; +//! +//! let mut buf = [b'0'; f64::FORMATTED_SIZE]; //! let slc = lexical_core::write::(15.1, &mut buf); //! assert_eq!(slc, b"15.1"); //! # } //! ``` //! //! # Conversion API -#![cfg_attr(feature = "write", doc = " **Write**")] -#![cfg_attr(feature = "write", doc = "")] -#![cfg_attr(feature = "write", doc = " - [`write`]")] -#![cfg_attr(feature = "write", doc = " - [`write_with_options`]")] -//! -#![cfg_attr(feature = "write", doc = " **From String**")] -#![cfg_attr(feature = "write", doc = "")] -#![cfg_attr(feature = "parse", doc = " - [`parse`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_partial`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_with_options`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_partial_with_options`]")] +//! +//! This writes and parses numbers to and from a format identical to +//! Rust's [`parse`][`core-parse`] and [`write`][`core-write`]. +//! +//! [`core-parse`]: core::str::FromStr::from_str +//! [`core-write`]: core::fmt::Display::fmt +//! +//! +#![cfg_attr(feature = "write", doc = "- [`write`]: Write a number to string.")] +#![cfg_attr( + feature = "parse", + doc = " +- [`parse`]: Parse a number from string validating the complete string is a number. +- [`parse_partial`]: Parse a number from string returning the number and the number + of digits it was able to parse. +" +)] +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats"))] { +//! # use core::str; +//! use lexical_core::FormattedSize; +//! +//! // parse +//! let f: f64 = lexical_core::parse(b"3.5").unwrap(); +//! assert_eq!(f, 3.5); +//! +//! let (f, count): (f64, usize) = lexical_core::parse_partial(b"3.5").unwrap(); +//! assert_eq!(f, 3.5); +//! assert_eq!(count, 3); +//! +//! // write +//! let mut buffer = [0u8; f64::FORMATTED_SIZE_DECIMAL]; +//! let digits = lexical_core::write(f, &mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("3.5")); +//! # } +//! ``` +//! +//! # Options/Formatting API +//! +//! Each number parser and writer contains extensive formatting control +//! through options and [`mod@format`] specifications, including digit +//! [`separator`] support (that is, numbers such as `1_2__3.4_5`), if +//! integral, fractional, or any significant digits are required, if to +//! disable parsing or writing of non-finite values, if `+` signs are +//! invalid or required, and much more. +//! +//! [`separator`]: NumberFormat::digit_separator +//! +//! +#![cfg_attr( + all(feature = "write", feature = "floats"), + doc = "[`nan_string`]: WriteFloatOptionsBuilder::nan_string" +)] +#![cfg_attr( + all(not(feature = "write"), feature = "parse", feature = "floats"), + doc = "[`nan_string`]: ParseFloatOptionsBuilder::nan_string" +)] +#![cfg_attr( + any(not(feature = "floats"), all(not(feature = "write"), not(feature = "parse"))), + doc = "[`nan_string`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.nan_string" +)] +//! +//! +#![cfg_attr( + feature = "write", + doc = "- [`write_with_options`]: Write a number to string using custom formatting options." +)] +#![cfg_attr( + feature = "parse", + doc = " +- [`parse_with_options`]: Parse a number from string using custom formatting options, + validating the complete string is a number. +- [`parse_partial_with_options`]: Parse a number from string using custom formatting + options, returning the number and the number of digits it was able to parse. +" +)] +//! +//! Some options, such as custom string representations of non-finite +//! floats (such as [`NaN`][`nan_string`]), are available without the +//! [`format`](crate#format) feature. For more comprehensive examples, see the +//! [`format`](#format) and [Comprehensive Configuration] sections +//! below. +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "format"))] { +//! # use core::str; +//! use lexical_core::{format, parse_float_options, write_float_options, FormattedSize}; +//! +//! // parse +//! let f: f64 = lexical_core::parse_with_options::<_, { format::JSON }>( +//! b"3.5", +//! &parse_float_options::JSON +//! ).unwrap(); +//! +//! // write +//! const BUFFER_SIZE: usize = write_float_options:: +//! JSON.buffer_size_const::(); +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! let digits = lexical_core::write_with_options::<_, { format::JSON }>( +//! f, +//! &mut buffer, +//! &write_float_options::JSON +//! ); +//! assert_eq!(str::from_utf8(digits), Ok("3.5")); +//! # } +//! ``` +//! +//! [Comprehensive Configuration]: #comprehensive-configuration //! //! # Features //! //! In accordance with the Rust ethos, all features are additive: the crate -//! may be build with `--all-features` without issue. The following features +//! may be build with `--all-features` without issue. The following features //! are enabled by default: //! -//! * `std` -//! * `write-integers` -//! * `write-floats` -//! * `parse-integers` -//! * `parse-floats` +//! * `write-integers` (Default) - Enable writing of integers. +//! * `write-floats` (Default) - Enable writing of floats. +//! * `parse-integers` (Default) - Enable parsing of integers. +//! * `parse-floats` (Default) - Enable parsing of floats. +//! * `power-of-two` - Add support for writing power-of-two number strings. +//! * `radix` - Add support for strings of any radix. +//! * `compact` - Reduce code size at the cost of performance. +//! * `format` - Add support for custom number formatting. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. +//! +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format //! //! A complete description of supported features includes: //! -//! #### std -//! -//! Enable use of the standard library. Currently, the standard library -//! is not used for any functionality, and may be disabled without any -//! change in functionality on stable. -//! //! #### write-integers //! //! Enable support for writing integers to string. //! +//! ```rust +//! # #[cfg(feature = "write-integers")] { +//! # use core::str; +//! use lexical_core::FormattedSize; +//! +//! let mut buffer = [0u8; i64::FORMATTED_SIZE_DECIMAL]; +//! let digits = lexical_core::write(1234, &mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("1234")); +//! # } +//! ``` +//! //! #### write-floats //! //! Enable support for writing floating-point numbers to string. //! +//! ```rust +//! # #[cfg(feature = "write-floats")] { +//! # use core::str; +//! use lexical_core::FormattedSize; +//! +//! let mut buffer = [0u8; f64::FORMATTED_SIZE_DECIMAL]; +//! let digits = lexical_core::write(1.234, &mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("1.234")); +//! # } +//! ``` +//! //! #### parse-integers //! //! Enable support for parsing integers from string. //! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { +//! let f: i64 = lexical_core::parse(b"1234").unwrap(); +//! assert_eq!(f, 1234); +//! # } +//! ``` +//! //! #### parsing-floats //! //! Enable support for parsing floating-point numbers from string. //! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { +//! let f: f64 = lexical_core::parse(b"1.234").unwrap(); +//! assert_eq!(f, 1.234); +//! # } +//! ``` +//! //! #### format //! -//! Adds support for the entire format API (using [`NumberFormatBuilder`]). -//! This allows extensive configurability for parsing and writing numbers +//! Adds support for the entire format [API][NumberFormatBuilder]. This +//! allows extensive configurability for parsing and writing numbers //! in custom formats, with different valid syntax requirements. //! +//! ##### JSON +//! //! For example, in JSON, the following floats are valid or invalid: //! //! ```text @@ -145,70 +323,126 @@ //! Infinity // invalid //! ``` //! -//! All of the finite numbers are valid in Rust, and Rust provides constants -//! for non-finite floats. In order to parse standard-conforming JSON floats -//! using lexical, you may use the following approach: +//! All of the finite numbers are valid in Rust, and Rust supports non-finite +//! floats. In order to parse standard-conforming JSON floats using +//! `lexical-core`, you may use the following approach: //! //! ```rust //! # #[cfg(all(feature = "parse-floats", feature = "format"))] { -//! use lexical_core::{format, parse_with_options, ParseFloatOptions, Result}; +//! use lexical_core::{format, parse_float_options, parse_with_options, Result}; //! //! fn parse_json_float>(bytes: Bytes) -> Result { -//! let options = ParseFloatOptions::new(); -//! parse_with_options::<_, { format::JSON }>(bytes.as_ref(), &options) +//! parse_with_options::<_, { format::JSON }>(bytes.as_ref(), &parse_float_options::JSON) //! } //! # } //! ``` //! -//! See the [Number Format](#number-format) section below for more information. +//! Enabling the [`format`](crate#format) API significantly increases compile +//! times, however, it enables a large amount of customization in how floats are +//! written. //! //! #### power-of-two //! -//! Enable doing numeric conversions to and from strings with power-of-two -//! radixes. This avoids most of the overhead and binary bloat of the radix -//! feature, while enabling support for the most commonly-used radixes. +//! Enable doing numeric conversions to and from strings radixes that are powers +//! of two, that is, `2`, `4`, `8`, `16`, and `32`. This avoids most of the +//! overhead and binary bloat of the [`radix`](#radix) feature, while enabling +//! support for the most commonly-used radixes. +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "power-of-two"))] { +//! # use core::str; +//! use lexical_core::{ +//! ParseFloatOptions, +//! WriteFloatOptions, +//! FormattedSize, +//! NumberFormatBuilder +//! }; +//! +//! // parse +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! let value = "1.0011101111100111011011001000101101000011100101011"; +//! let f: f64 = lexical_core::parse_with_options::<_, { BINARY }>( +//! value.as_bytes(), +//! &ParseFloatOptions::new() +//! ).unwrap(); +//! +//! // write +//! let mut buffer = [0u8; f64::FORMATTED_SIZE]; +//! let digits = lexical_core::write_with_options::<_, { BINARY }>( +//! f, +//! &mut buffer, +//! &WriteFloatOptions::new() +//! ); +//! assert_eq!(str::from_utf8(digits), Ok(value)); +//! # } +//! ``` //! //! #### radix //! //! Enable doing numeric conversions to and from strings for all radixes. -//! This requires substantially more static storage than `power-of-two`, -//! and increases compile times by a fair amount, but can be quite useful +//! This requires more static storage than [`power-of-two`](#power-of-two), +//! and increases compile times, but can be quite useful //! for esoteric programming languages which use duodecimal floats, for //! example. //! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "radix"))] { +//! # use core::str; +//! use lexical_core::{ +//! ParseFloatOptions, +//! WriteFloatOptions, +//! FormattedSize, +//! NumberFormatBuilder +//! }; +//! +//! // parse +//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12); +//! let value = "1.29842830A44BAA2"; +//! let f: f64 = lexical_core::parse_with_options::<_, { FORMAT }>( +//! value.as_bytes(), +//! &ParseFloatOptions::new() +//! ).unwrap(); +//! +//! // write +//! let mut buffer = [0u8; f64::FORMATTED_SIZE]; +//! let digits = lexical_core::write_with_options::<_, { FORMAT }>( +//! f, +//! &mut buffer, +//! &WriteFloatOptions::new() +//! ); +//! assert_eq!(str::from_utf8(digits), Ok(value)); +//! # } +//! ``` +//! //! #### compact //! //! Reduce the generated code size at the cost of performance. This minimizes //! the number of static tables, inlining, and generics used, drastically //! reducing the size of the generated binaries. //! -//! #### safe +//! #### std //! -//! This replaces most unchecked indexing, required in cases where the -//! compiler cannot elide the check, with checked indexing. However, -//! it does not fully replace all unsafe behavior with safe behavior. -//! To minimize the risk of undefined behavior and out-of-bounds reads/writers, -//! extensive edge-cases, property-based tests, and fuzzing is done with both -//! the safe feature enabled and disabled, with the tests verified by Miri -//! and Valgrind. +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. //! -//! # Configuration API +//! # Comprehensive Configuration //! -//! Lexical provides two main levels of configuration: +//! `lexical-core` provides two main levels of configuration: //! - The [`NumberFormatBuilder`], creating a packed struct with custom //! formatting options. //! - The Options API. //! //! ## Number Format //! -//! The number format class provides numerous flags to specify -//! number parsing or writing. When the `power-of-two` feature is +//! The number format class provides numerous flags to specify number parsing or +//! writing. When the [`power-of-two`](#power-of-two) feature is //! enabled, additional flags are added: //! - The radix for the significant digits (default `10`). //! - The radix for the exponent base (default `10`). //! - The radix for the exponent digits (default `10`). //! -//! When the `format` feature is enabled, numerous other syntax and +//! When the [`format`](#format) feature is enabled, numerous other syntax and //! digit separator flags are enabled, including: //! - A digit separator character, to group digits for increased legibility. //! - Whether leading, trailing, internal, and consecutive digit separators are @@ -219,22 +453,87 @@ //! //! Many pre-defined constants therefore exist to simplify common use-cases, //! including: -//! - `JSON`, `XML`, `TOML`, `YAML`, `SQLite`, and many more. -//! - `Rust`, `Python`, `C#`, `FORTRAN`, `COBOL` literals and strings, and many -//! more. +//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, +//! and many more. +//! +//! For a list of all supported fields, see +//! [Fields][NumberFormatBuilder#fields-1]. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`JSON`]: format::JSON +[`XML`]: format::XML +[`TOML`]: format::TOML +[`YAML`]: format::YAML +[`SQLite`]: format::SQLITE +[`Rust`]: format::RUST_LITERAL +[`Python`]: format::PYTHON_LITERAL +[`C#`]: format::CSHARP_LITERAL +[`FORTRAN`]: format::FORTRAN_LITERAL +[`COBOL`]: format::COBOL_LITERAL +" +)] +#![cfg_attr( + not(feature = "format"), + doc = " +[`JSON`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical-core/latest/lexical_core/format/constant.COBOL_LITERAL.html +" +)] //! //! ## Options API //! //! The Options API provides high-level options to specify number parsing //! or writing, options not intrinsically tied to a number format. //! For example, the Options API provides: -//! - The exponent character (default `b'e'`, or `b'^'`). -//! - The decimal point character (default `b'.'`). -//! - Custom `NaN`, `Infinity` string representations. -//! - Whether to trim the fraction component from integral floats. -//! - The exponent break point for scientific notation. -//! - The maximum and minimum number of significant digits to write. -//! - The rounding mode when truncating significant digits while writing. +//! +//! - The [`exponent`][`write-float-exponent`] character (defaults to `b'e'` or `b'^'`, depending on the radix). +//! - The [`decimal point`][`write-float-decimal_point`] character (defaults to `b'.'`). +//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string +//! [`representations`][`write-float-nan_string`]. +//! - Whether to [`trim`][`write-float-trim_floats`] the fraction component from integral floats. +//! - The exponent [`break-point`][`write-float-positive_exponent_break`] for scientific notation. +//! - The [`maximum`][`write-float-max_significant_digits`] and [`minimum`][`write-float-min_significant_digits`] number of significant digits to write. +//! - The rounding [`mode`][`write-float-round_mode`] when truncating significant digits while writing. +//! +//! +#![cfg_attr( + feature = "write-floats", + doc = " +[`write-float-exponent`]: WriteFloatOptionsBuilder::exponent +[`write-float-decimal_point`]: WriteFloatOptionsBuilder::decimal_point +[`write-float-nan_string`]: WriteFloatOptionsBuilder::nan_string +[`write-float-trim_floats`]: WriteFloatOptionsBuilder::trim_floats +[`write-float-positive_exponent_break`]: WriteFloatOptionsBuilder::positive_exponent_break +[`write-float-max_significant_digits`]: WriteFloatOptionsBuilder::max_significant_digits +[`write-float-min_significant_digits`]: WriteFloatOptionsBuilder::min_significant_digits +[`write-float-round_mode`]: WriteFloatOptionsBuilder::round_mode +" +)] +#![cfg_attr( + not(feature = "write-floats"), + doc = " +[`write-float-exponent`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.exponent +[`write-float-decimal_point`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.decimal_point +[`write-float-nan_string`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.nan_string +[`write-float-trim_floats`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.trim_floats +[`write-float-positive_exponent_break`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.positive_exponent_break +[`write-float-max_significant_digits`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.max_significant_digits +[`write-float-min_significant_digits`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.min_significant_digits +[`write-float-round_mode`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.round_mode +" +)] //! //! The available options are: #![cfg_attr(feature = "parse-floats", doc = " - [`ParseFloatOptions`]")] @@ -243,47 +542,58 @@ #![cfg_attr(feature = "write-integers", doc = " - [`WriteIntegerOptions`]")] //! //! In addition, pre-defined constants for each category of options may -//! be found in their respective modules. +//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`]. //! -//! ## Example +//! +#![cfg_attr(feature = "parse-floats", doc = "[`JSON-OPTS`]: parse_float_options::JSON")] +#![cfg_attr( + not(feature = "parse-floats"), + doc = "[`JSON-OPTS`]: https://docs.rs/lexical-core/latest/lexical_core/parse_float_options/constant.JSON.html" +)] +//! +//! ## Examples //! //! An example of creating your own options to parse European-style //! numbers (which use commas as decimal points, and periods as digit //! separators) is as follows: //! //! ``` -//! # pub fn main() { -//! # #[cfg(all(feature = "parse_floats", feature = "format"))] { +//! # #[cfg(all(feature = "parse-floats", feature = "format"))] { +//! # use core::num; //! // This creates a format to parse a European-style float number. //! // The decimal point is a comma, and the digit separators (optional) //! // are periods. //! const EUROPEAN: u128 = lexical_core::NumberFormatBuilder::new() -//! .digit_separator(b'.') -//! .build(); -//! let options = lexical_core::ParseFloatOptions::builder() +//! .digit_separator(num::NonZeroU8::new(b'.')) +//! .build_strict(); +//! const COMMA_OPTIONS: lexical_core::ParseFloatOptions = lexical_core::ParseFloatOptions::builder() //! .decimal_point(b',') -//! .build() -//! .unwrap(); +//! .build_strict(); //! assert_eq!( -//! lexical_core::parse_with_options::(b"300,10", &options), +//! lexical_core::parse_with_options::(b"300,10", &COMMA_OPTIONS), //! Ok(300.10) //! ); //! //! // Another example, using a pre-defined constant for JSON. //! const JSON: u128 = lexical_core::format::JSON; -//! let options = lexical_core::ParseFloatOptions::new(); +//! const JSON_OPTIONS: lexical_core::ParseFloatOptions = lexical_core::ParseFloatOptions::new(); //! assert_eq!( -//! lexical_core::parse_with_options::(b"0e1", &options), +//! lexical_core::parse_with_options::(b"0e1", &JSON_OPTIONS), //! Ok(0.0) //! ); //! assert_eq!( -//! lexical_core::parse_with_options::(b"1E+2", &options), +//! lexical_core::parse_with_options::(b"1E+2", &JSON_OPTIONS), //! Ok(100.0) //! ); //! # } -//! # } //! ``` //! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! //! # Algorithms //! //! - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) @@ -308,38 +618,66 @@ //! - [Build Timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md) //! - [Digit Separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) //! -//! # Version Support -//! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. +//! # Safety Guarantees //! -//! # Safety -//! -//! There is no non-trivial unsafe behavior in [lexical][crate] itself, +//! There is no non-trivial unsafe behavior in `lexical-core` itself, //! however, any incorrect safety invariants in our parsers and writers -//! (`lexical-parse-float`, `lexical-parse-integer`, `lexical-write-float`, -//! and `lexical-write-integer`) could cause those safety invariants to -//! be broken. +//! ([`lexical-parse-float`], [`lexical-parse-integer`], +//! [`lexical-write-float`], and [`lexical-write-integer`]) could cause those +//! safety invariants to be broken. +//! +//! +#![cfg_attr( + feature = "write", + doc = " +[`write`]: crate::write +[`write_with_options`]: crate::write_with_options +" +)] +#![cfg_attr( + not(feature = "write"), + doc = " +[`write`]: https://docs.rs/lexical-core/latest/lexical_core/fn.write.html +[`write_with_options`]: https://docs.rs/lexical-core/latest/lexical_core/fn.write_with_options.html +" +)] +#![cfg_attr( + feature = "parse", + doc = " +[`parse`]: crate::parse +[`parse_partial`]: crate::parse_partial +[`parse_with_options`]: crate::parse_with_options +[`parse_partial_with_options`]: crate::parse_partial_with_options +" +)] +#![cfg_attr( + not(feature = "parse"), + doc = " +[`parse`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse.html +[`parse_partial`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse_partial.html +[`parse_with_options`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse_with_options.html +[`parse_partial_with_options`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse_partial_with_options.html +" +)] //! -//! [`write`]: crate::write -//! [`write_with_options`]: crate::write_with_options -//! [`parse`]: crate::parse -//! [`parse_partial`]: crate::parse_partial -//! [`parse_with_options`]: crate::parse_with_options -//! [`parse_partial_with_options`]: crate::parse_partial_with_options +//! +#![cfg_attr(feature = "parse-floats", doc = "[`ParseFloatOptions`]: crate::ParseFloatOptions")] +#![cfg_attr(feature = "parse-integers", doc = "[`ParseIntegerOptions`]: crate::ParseIntegerOptions")] +#![cfg_attr(feature = "write-floats", doc = "[`WriteFloatOptions`]: crate::WriteFloatOptions")] +#![cfg_attr(feature = "write-integers", doc = "[`WriteIntegerOptions`]: crate::WriteIntegerOptions")] //! +//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html //! [`NumberFormatBuilder`]: crate::NumberFormatBuilder -//! [`ParseFloatOptions`]: crate::ParseFloatOptions -//! [`ParseIntegerOptions`]: crate::ParseIntegerOptions -//! [`WriteFloatOptions`]: crate::WriteFloatOptions -//! [`WriteIntegerOptions`]: crate::WriteIntegerOptions -//! [benchmarks]: +//! [benchmarks]: https://github.com/Alexhuszagh/lexical-benchmarks +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -364,14 +702,64 @@ // we use this for inline formatting for unsafe blocks clippy::semicolon_inside_block, )] +#![cfg_attr(rustfmt, rustfmt_skip)] // reason = "this simplifies our imports" + +// Ensure our features are properly enabled. This means no parse without +// parse support, etc. +#[cfg(all(feature = "parse", not(any(feature = "parse-integers", feature = "parse-floats"))))] +compile_error!( + "Do not use the `parse` feature directly. Use `parse-integers` and/or `parse-floats` instead." +); + +#[cfg(all(feature = "write", not(any(feature = "write-integers", feature = "write-floats"))))] +compile_error!( + "Do not use the `write` feature directly. Use `write-integers` and/or `write-floats` instead." +); + +#[cfg(all(feature = "integers", not(any(feature = "write-integers", feature = "parse-integers"))))] +compile_error!("Do not use the `integers` feature directly. Use `write-integers` and/or `parse-integers` instead."); + +#[cfg(all(feature = "floats", not(any(feature = "write-floats", feature = "parse-floats"))))] +compile_error!( + "Do not use the `floats` feature directly. Use `write-floats` and/or `parse-floats` instead." +); // Re-exports +pub use lexical_util::Error; +pub use lexical_util::result::Result; + +pub use lexical_util::format::{ + self, + // FIXME: Do not export in the next breaking release. + format_error, + // FIXME: Do not export in the next breaking release. + format_is_valid, + NumberFormat, + NumberFormatBuilder, +}; + +#[cfg(feature = "f16")] +pub use lexical_util::bf16::bf16; + +#[cfg(feature = "f16")] +pub use lexical_util::f16::f16; + +// PARSE + +#[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +pub use lexical_util::options::ParseOptions; + +#[cfg(feature = "parse")] +use lexical_util::{from_lexical, from_lexical_with_options}; + #[cfg(feature = "parse-floats")] pub use lexical_parse_float::{ options as parse_float_options, Options as ParseFloatOptions, OptionsBuilder as ParseFloatOptionsBuilder, }; + #[cfg(feature = "parse-floats")] use lexical_parse_float::{ FromLexical as FromFloat, @@ -388,25 +776,20 @@ use lexical_parse_integer::{ FromLexical as FromInteger, FromLexicalWithOptions as FromIntegerWithOptions, }; -#[cfg(feature = "f16")] -pub use lexical_util::bf16::bf16; -#[cfg(feature = "write")] -pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; -#[cfg(feature = "parse")] -pub use lexical_util::error::Error; -#[cfg(feature = "f16")] -pub use lexical_util::f16::f16; -pub use lexical_util::format::{self, format_error, format_is_valid, NumberFormatBuilder}; -#[cfg(feature = "parse")] -pub use lexical_util::options::ParseOptions; + +// WRITE + #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub use lexical_util::options::WriteOptions; -#[cfg(feature = "parse")] -pub use lexical_util::result::Result; -#[cfg(feature = "parse")] -use lexical_util::{from_lexical, from_lexical_with_options}; + #[cfg(feature = "write")] use lexical_util::{to_lexical, to_lexical_with_options}; + +#[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; + #[cfg(feature = "write-floats")] pub use lexical_write_float::{ options as write_float_options, @@ -415,12 +798,14 @@ pub use lexical_write_float::{ }; #[cfg(feature = "write-floats")] use lexical_write_float::{ToLexical as ToFloat, ToLexicalWithOptions as ToFloatWithOptions}; + #[cfg(feature = "write-integers")] pub use lexical_write_integer::{ options as write_integer_options, Options as WriteIntegerOptions, OptionsBuilder as WriteIntegerOptionsBuilder, }; + #[cfg(feature = "write-integers")] use lexical_write_integer::{ToLexical as ToInteger, ToLexicalWithOptions as ToIntegerWithOptions}; @@ -428,13 +813,40 @@ use lexical_write_integer::{ToLexical as ToInteger, ToLexicalWithOptions as ToIn // --- #[cfg(feature = "parse")] -from_lexical!(); +from_lexical!( + "lexical_core", + 1234, + u64, + 4, + #[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +); + #[cfg(feature = "parse")] -from_lexical_with_options!(); +from_lexical_with_options!( + "lexical_core", + 1234, + u64, + 4, + ParseIntegerOptions, + #[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +); + #[cfg(feature = "write")] -to_lexical!(); +to_lexical!( + "lexical_core", + 1234, + u64, + #[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +); + #[cfg(feature = "write")] -to_lexical_with_options!(); +to_lexical_with_options!( + "lexical_core", + 1234, + u64, + WriteIntegerOptions, + #[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +); /// Implement `FromLexical` and `FromLexicalWithOptions` for numeric types. /// @@ -571,11 +983,10 @@ float_to_lexical! { f32 f64 } /// number. In order to ensure the function will not panic, provide a /// buffer with at least `{integer}::FORMATTED_SIZE` elements. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(feature = "write-floats")] { +/// # #[cfg(feature = "write-floats")] { /// // import `BUFFER_SIZE` to get the maximum bytes written by the number. /// use lexical_core::BUFFER_SIZE; /// @@ -586,7 +997,6 @@ float_to_lexical! { f32 f64 } /// /// assert_eq!(&buffer[0..9], b"3.1415927"); /// # } -/// # } /// ``` /// /// This will panic, because the buffer is not large enough: @@ -605,6 +1015,7 @@ float_to_lexical! { f32 f64 } /// ``` #[inline] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { n.to_lexical(bytes) } @@ -633,11 +1044,10 @@ pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { /// ensure `is_valid()` is called prior to using the format, or checking /// its validity using a static assertion. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(feature = "write-floats")] { +/// # #[cfg(feature = "write-floats")] { /// // import `BUFFER_SIZE` to get the maximum bytes written by the number. /// use lexical_core::BUFFER_SIZE; /// @@ -645,12 +1055,11 @@ pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { /// let float = 3.14159265359_f32; /// /// const FORMAT: u128 = lexical_core::format::STANDARD; -/// let options = lexical_core::WriteFloatOptions::new(); -/// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &options); +/// const OPTIONS: lexical_core::WriteFloatOptions = lexical_core::WriteFloatOptions::new(); +/// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &OPTIONS); /// /// assert_eq!(&buffer[0..9], b"3.1415927"); /// # } -/// # } /// ``` /// /// This will panic, because the buffer is not large enough: @@ -662,8 +1071,8 @@ pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { /// let float = 3.14159265359_f32; /// /// const FORMAT: u128 = lexical_core::format::STANDARD; -/// let options = lexical_core::WriteFloatOptions::new(); -/// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &options); +/// const OPTIONS: lexical_core::WriteFloatOptions = lexical_core::WriteFloatOptions::new(); +/// lexical_core::write_with_options::<_, FORMAT>(float, &mut buffer, &OPTIONS); /// # } /// # #[cfg(not(feature = "write-floats"))] { /// # panic!(""); @@ -671,6 +1080,7 @@ pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { /// ``` #[inline] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn write_with_options<'a, N: ToLexicalWithOptions, const FORMAT: u128>( n: N, bytes: &'a mut [u8], @@ -686,19 +1096,18 @@ pub fn write_with_options<'a, N: ToLexicalWithOptions, const FORMAT: u128>( /// /// * `bytes` - Byte slice containing a numeric string. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(feature = "parse-floats")] { +/// # #[cfg(feature = "parse-floats")] { /// let string = "3.14159265359"; /// let result = lexical_core::parse::(string.as_bytes()); /// assert_eq!(result, Ok(3.14159265359_f32)); /// # } -/// # } /// ``` #[inline] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub fn parse(bytes: &[u8]) -> Result { N::from_lexical(bytes) } @@ -711,19 +1120,18 @@ pub fn parse(bytes: &[u8]) -> Result { /// /// * `bytes` - Byte slice containing a numeric string. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(feature = "parse-floats")] { +/// # #[cfg(feature = "parse-floats")] { /// let string = "3.14159265359 hello"; /// let result = lexical_core::parse_partial::(string.as_bytes()); /// assert_eq!(result, Ok((3.14159265359_f32, 13))); /// # } -/// # } /// ``` #[inline] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub fn parse_partial(bytes: &[u8]) -> Result<(N, usize)> { N::from_lexical_partial(bytes) } @@ -737,21 +1145,20 @@ pub fn parse_partial(bytes: &[u8]) -> Result<(N, usize)> { /// * `bytes` - Byte slice containing a numeric string. /// * `options` - Options to customize number parsing. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(all(feature = "parse-floats", feature = "format"))] { +/// # #[cfg(all(feature = "parse-floats", feature = "format"))] { /// const JSON: u128 = lexical_core::format::JSON; -/// let options = lexical_core::ParseFloatOptions::new(); +/// const OPTIONS: lexical_core::ParseFloatOptions = lexical_core::ParseFloatOptions::new(); /// let string = "3.14159265359"; -/// let result = lexical_core::parse_with_options::(string.as_bytes(), &options); +/// let result = lexical_core::parse_with_options::(string.as_bytes(), &OPTIONS); /// assert_eq!(result, Ok(3.14159265359_f32)); /// # } -/// # } /// ``` #[inline] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub fn parse_with_options( bytes: &[u8], options: &N::Options, @@ -769,21 +1176,20 @@ pub fn parse_with_options( /// * `bytes` - Byte slice containing a numeric string. /// * `options` - Options to customize number parsing. /// -/// # Example +/// # Examples /// /// ``` -/// # pub fn main() { -/// #[cfg(all(feature = "parse-floats", feature = "format"))] { +/// # #[cfg(all(feature = "parse-floats", feature = "format"))] { /// const JSON: u128 = lexical_core::format::JSON; -/// let options = lexical_core::ParseFloatOptions::new(); +/// const OPTIONS: lexical_core::ParseFloatOptions = lexical_core::ParseFloatOptions::new(); /// let string = "3.14159265359 hello"; -/// let result = lexical_core::parse_partial_with_options::(string.as_bytes(), &options); +/// let result = lexical_core::parse_partial_with_options::(string.as_bytes(), &OPTIONS); /// assert_eq!(result, Ok((3.14159265359_f32, 13))); /// # } -/// # } /// ``` #[inline] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub fn parse_partial_with_options( bytes: &[u8], options: &N::Options, diff --git a/lexical-core/tests/api_tests.rs b/lexical-core/tests/api_tests.rs index 0815b1aa..80f42179 100644 --- a/lexical-core/tests/api_tests.rs +++ b/lexical-core/tests/api_tests.rs @@ -3,10 +3,10 @@ fn integer_to_string_test() { let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; assert_eq!(lexical_core::write(12345u32, &mut buffer), b"12345"); - let options = lexical_core::WriteIntegerOptions::new(); + const OPTIONS: lexical_write_integer::Options = lexical_core::WriteIntegerOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!( - lexical_core::write_with_options::<_, FORMAT>(12345u32, &mut buffer, &options), + lexical_core::write_with_options::<_, FORMAT>(12345u32, &mut buffer, &OPTIONS), b"12345" ); } @@ -16,10 +16,10 @@ fn integer_to_string_test() { fn float_to_string_test() { let mut buffer = [b'0'; lexical_core::BUFFER_SIZE]; assert_eq!(lexical_core::write(12345.0f32, &mut buffer), b"12345.0"); - let options = lexical_core::WriteFloatOptions::new(); + const OPTIONS: lexical_write_float::Options = lexical_core::WriteFloatOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; assert_eq!( - lexical_core::write_with_options::<_, FORMAT>(12345.0f32, &mut buffer, &options), + lexical_core::write_with_options::<_, FORMAT>(12345.0f32, &mut buffer, &OPTIONS), b"12345.0" ); } @@ -30,11 +30,11 @@ fn string_to_integer_test() { assert_eq!(lexical_core::parse(b"12345"), Ok(12345u32)); assert_eq!(lexical_core::parse_partial(b"12345"), Ok((12345u32, 5))); - let options = lexical_core::ParseIntegerOptions::new(); + const OPTIONS: lexical_parse_integer::Options = lexical_core::ParseIntegerOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; - assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345", &options), Ok(12345u32)); + assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345", &OPTIONS), Ok(12345u32)); assert_eq!( - lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345", &options), + lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345", &OPTIONS), Ok((12345u32, 5)) ); } @@ -45,17 +45,18 @@ fn string_to_float_test() { assert_eq!(lexical_core::parse(b"12345.0"), Ok(12345.0f32)); assert_eq!(lexical_core::parse_partial(b"12345.0"), Ok((12345.0f32, 7))); - let options = lexical_core::ParseFloatOptions::new(); + const OPTIONS: lexical_parse_float::Options = lexical_core::ParseFloatOptions::new(); const FORMAT: u128 = lexical_core::format::STANDARD; - assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345.0", &options), Ok(12345.0f32)); + assert_eq!(lexical_core::parse_with_options::<_, FORMAT>(b"12345.0", &OPTIONS), Ok(12345.0f32)); assert_eq!( - lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345.0", &options), + lexical_core::parse_partial_with_options::<_, FORMAT>(b"12345.0", &OPTIONS), Ok((12345.0f32, 7)) ); } /// Test that converting the specified value into a buffer of FORMATTED_SIZE /// yields the expected string +#[cfg(feature = "write-integers")] macro_rules! test_format { ($t:ty, $value:expr, $expected:expr) => {{ use lexical_core::FormattedSize; diff --git a/lexical-core/tests/issue_97_tests.rs b/lexical-core/tests/issue_97_tests.rs index d5efbe67..3203cdec 100644 --- a/lexical-core/tests/issue_97_tests.rs +++ b/lexical-core/tests/issue_97_tests.rs @@ -9,7 +9,7 @@ fn issue_97_test() { const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) - .build(); + .build_strict(); let fopts = lexical_core::ParseFloatOptions::new(); let iopts = lexical_core::ParseIntegerOptions::new(); diff --git a/lexical-parse-float/Cargo.toml b/lexical-parse-float/Cargo.toml index 4d05512d..64ea10ea 100644 --- a/lexical-parse-float/Cargo.toml +++ b/lexical-parse-float/Cargo.toml @@ -10,7 +10,7 @@ name = "lexical-parse-float" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "1.0.5" -rust-version = "1.61.0" +rust-version = "1.60.0" exclude = [ "assets/*", "docs/*", @@ -30,9 +30,6 @@ path = "../lexical-parse-integer" default-features = false features = [] -[dependencies] -static_assertions = "1" - [features] default = ["std"] # Use the standard library. @@ -82,3 +79,4 @@ f128 = ["lexical-util/f128"] [package.metadata.docs.rs] features = ["radix", "format", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-parse-float/etc/correctness/test-parse-golang/main.rs b/lexical-parse-float/etc/correctness/test-parse-golang/main.rs index f47161f2..a0121ca1 100644 --- a/lexical-parse-float/etc/correctness/test-parse-golang/main.rs +++ b/lexical-parse-float/etc/correctness/test-parse-golang/main.rs @@ -40,7 +40,7 @@ fn run_test(line: &str, rng: &mut Random) { let hex32 = line[5..13].to_lowercase(); let hex64 = line[14..30].to_lowercase(); let string = &line[31..]; - let options = Options::new(); + const OPTIONS: Options = Options::new(); // now we want to add the number of digit separators we'll use let count = rng.gen_range(1..=4); @@ -55,8 +55,8 @@ fn run_test(line: &str, rng: &mut Random) { let string = str::from_utf8(&vec).unwrap(); let valid = SIGN.replace(string, "${2}${1}"); - let float32 = f32::from_lexical_with_options::(valid.as_bytes(), &options).unwrap(); - let float64 = f64::from_lexical_with_options::(valid.as_bytes(), &options).unwrap(); + let float32 = f32::from_lexical_with_options::(valid.as_bytes(), &OPTIONS).unwrap(); + let float64 = f64::from_lexical_with_options::(valid.as_bytes(), &OPTIONS).unwrap(); assert_eq!(hex32, format!("{:0>8x}", float32.to_bits())); assert_eq!(hex64, format!("{:0>16x}", float64.to_bits())); } @@ -74,10 +74,10 @@ fn run_test(line: &str, _: &mut Random) { let hex32 = line[5..13].to_lowercase(); let hex64 = line[14..30].to_lowercase(); let string = &line[31..]; - let options = Options::new(); + const OPTIONS: Options = Options::new(); - let float32 = f32::from_lexical_with_options::(string.as_bytes(), &options).unwrap(); - let float64 = f64::from_lexical_with_options::(string.as_bytes(), &options).unwrap(); + let float32 = f32::from_lexical_with_options::(string.as_bytes(), &OPTIONS).unwrap(); + let float64 = f64::from_lexical_with_options::(string.as_bytes(), &OPTIONS).unwrap(); assert_eq!(hex32, format!("{:0>8x}", float32.to_bits())); assert_eq!(hex64, format!("{:0>16x}", float64.to_bits())); } diff --git a/lexical-parse-float/src/api.rs b/lexical-parse-float/src/api.rs index aa9639f4..330cf811 100644 --- a/lexical-parse-float/src/api.rs +++ b/lexical-parse-float/src/api.rs @@ -71,8 +71,8 @@ macro_rules! float_from_lexical { )*) } -from_lexical! {} -from_lexical_with_options! {} +from_lexical!("lexical_parse_float", 1.234, f64, 5); +from_lexical_with_options!("lexical_parse_float", 1.234, f64, 5, Options); float_from_lexical! { f32 f64 } #[cfg(feature = "f16")] diff --git a/lexical-parse-float/src/lib.rs b/lexical-parse-float/src/lib.rs index 208501e8..ad36d3f4 100644 --- a/lexical-parse-float/src/lib.rs +++ b/lexical-parse-float/src/lib.rs @@ -1,5 +1,428 @@ //! Fast lexical string-to-float conversion routines. //! +//! This contains high-performance methods to parse floats from bytes. +//! Using [`from_lexical`] is analogous to [`parse`][`core-parse`], +//! while enabling parsing from bytes as well as [`str`]. +//! +//! +//! [`from_lexical`]: FromLexical::from_lexical +//! [`core-parse`]: core::str::FromStr +//! +//! # Getting Started +//! +//! To parse a number from bytes, use [`from_lexical`]: +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_parse_float::{Error, FromLexical}; +//! +//! let value = f64::from_lexical("1234.5".as_bytes()); +//! assert_eq!(value, Ok(1234.5)); +//! +//! let value = f64::from_lexical("1.2345e325".as_bytes()); +//! assert_eq!(value, Ok(f64::INFINITY)); +//! +//! let value = f64::from_lexical("1234.5 }, {\"Key\", \"Value\"}}".as_bytes()); +//! assert_eq!(value, Err(Error::InvalidDigit(6))); +//! ``` +//! +//! If wishing to incrementally parse a string from bytes, that is, parse as +//! many characters until an invalid digit is found, you can use the partial +//! parsers. This is useful in parsing data where the type is known, such as +//! JSON, but where the end of the number is not yet known. +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_parse_float::{Error, FromLexical}; +//! +//! let value = f64::from_lexical_partial("1234.5 }, {\"Key\", \"Value\"}}".as_bytes()); +//! assert_eq!(value, Ok((1234.5, 6))); +//! +//! let value = f64::from_lexical_partial("1.2345e325".as_bytes()); +//! assert_eq!(value, Ok((f64::INFINITY, 10))); +//! ``` +//! +//! # Options/Formatting API +//! +//! Each float parser contains extensive formatting control through +//! [`mod@options`] and [`mod@format`], including digit [`separator`] +//! support (that is, floats such as `1_2__3.4_5`), if integral, +//! fractional, or any significant digits are required, if to disable +//! parsing non-finite values, if `+` signs are invalid or required, +//! and much more. For more comprehensive examples, see the +//! [`format`](#format) and [Comprehensive Configuration] sections +//! below. +//! +//! [`separator`]: NumberFormat::digit_separator +//! [Comprehensive Configuration]: #comprehensive-configuration +//! +//! ```rust +//! # #[cfg(feature = "radix")] { +//! # use core::str; +//! use lexical_parse_float::{Error, FromLexicalWithOptions, NumberFormatBuilder, Options}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::new() +//! // require a `+` or `-` sign before the number +//! .required_mantissa_sign(true) +//! // require a `+` or `-` sign before the exponent digits +//! .required_exponent_sign(true) +//! // build the format, panicking if the format is invalid +//! .build_strict(); +//! const OPTIONS: Options = Options::new(); +//! +//! let value = "+1.234e+300"; +//! let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Ok(1.234e+300)); +//! +//! let value = "1.234e+300"; +//! let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Err(Error::MissingSign(0))); +//! # } +//! ``` +//! +//! # Features +//! +//! * `format` - Add support for parsing custom integer formats. +//! * `power-of-two` - Add support for parsing power-of-two integer strings. +//! * `radix` - Add support for strings of any radix. +//! * `compact` - Reduce code size at the cost of performance. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. +//! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format +//! +//! A complete description of supported features includes: +//! +//! #### format +//! +//! Add support custom float parsing specifications. This should be used in +//! conjunction with [`Options`] for extensible float parsing. +//! +//! ##### JSON +//! +//! For example, in JSON, the following floats are valid or invalid: +//! +//! ```text +//! -1 // valid +//! +1 // invalid +//! 1 // valid +//! 1. // invalid +//! .1 // invalid +//! 0.1 // valid +//! nan // invalid +//! inf // invalid +//! Infinity // invalid +//! ``` +//! +//! All of the finite numbers are valid in Rust, and Rust provides constants +//! for non-finite floats. In order to parse standard-conforming JSON floats +//! using lexical, you may use the following approach: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! use lexical_parse_float::{format, options, Error, FromLexicalWithOptions, Result}; +//! +//! fn parse_json_float(bytes: &[u8]) -> Result { +//! f64::from_lexical_with_options::<{ format::JSON }>(bytes, &options::JSON) +//! } +//! +//! assert_eq!(parse_json_float(b"-1"), Ok(-1.0)); +//! assert_eq!(parse_json_float(b"+1"), Err(Error::InvalidPositiveSign(0))); +//! assert_eq!(parse_json_float(b"1"), Ok(1.0)); +//! assert_eq!(parse_json_float(b"1."), Err(Error::EmptyFraction(2))); +//! assert_eq!(parse_json_float(b"0.1"), Ok(0.1)); +//! assert_eq!(parse_json_float(b"nan"), Err(Error::EmptyInteger(0))); +//! assert_eq!(parse_json_float(b"inf"), Err(Error::EmptyInteger(0))); +//! assert_eq!(parse_json_float(b"Infinity"), Err(Error::EmptyInteger(0))); +//! # } +//! ``` +//! +//! ##### Custom Format +//! +//! An example building and using a custom format, with many of the available +//! options is: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! # use core::{num, str}; +//! use lexical_parse_float::{Error, NumberFormatBuilder, Options, FromLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::new() +//! // enable the use of digit separators with `_` +//! .digit_separator(num::NonZeroU8::new(b'_')) +//! // require digits before and after the decimal point, +//! // if the decimal point is present. +//! .required_integer_digits(true) +//! .required_fraction_digits(true) +//! // do not allow a leading `+` sign, so `+123` is invalid +//! .no_positive_mantissa_sign(true) +//! // do not allow `0` before an integer, so `01.1` is invalid. +//! // however, `0.1` is valid. +//! .no_integer_leading_zeros(true) +//! // allow digit separators anywhere, including consecutive ones +//! .leading_digit_separator(true) +//! .trailing_digit_separator(true) +//! .internal_digit_separator(true) +//! .consecutive_digit_separator(true) +//! // make it so the exponent character, `e`, is case-sensitive +//! // that is, `E` is not considered a valid exponent character +//! .case_sensitive_exponent(true) +//! .build_strict(); +//! const OPTIONS: Options = Options::builder() +//! // change the string representation of NaN from `NaN` to `nan` +//! .nan_string(Some(b"nan")) +//! // disable a short infinity: long infinity is still allowed +//! .inf_string(None) +//! .build_strict(); +//! +//! let value = f64::from_lexical_with_options::(b"1_2.3_4", &OPTIONS); +//! assert_eq!(value, Ok(12.34)); +//! +//! let value = f64::from_lexical_with_options::(b"-inf", &OPTIONS); +//! assert_eq!(value, Err(Error::EmptyInteger(1))); +//! +//! let value = f64::from_lexical_with_options::(b"Infinity", &OPTIONS); +//! assert_eq!(value, Ok(f64::INFINITY)); +//! +//! let value = f64::from_lexical_with_options::(b"nan", &OPTIONS); +//! assert_eq!(value.map(|x| x.is_nan()), Ok(true)); +//! +//! let value = f64::from_lexical_with_options::(b"+1_2.3_4", &OPTIONS); +//! assert_eq!(value, Err(Error::InvalidPositiveSign(0))); +//! +//! let value = f64::from_lexical_with_options::(b"0.3_4", &OPTIONS); +//! assert_eq!(value, Ok(0.34)); +//! +//! let value = f64::from_lexical_with_options::(b"12", &OPTIONS); +//! assert_eq!(value, Ok(12.0)); +//! +//! let value = f64::from_lexical_with_options::(b"12.", &OPTIONS); +//! assert_eq!(value, Err(Error::EmptyFraction(3))); +//! +//! let value = f64::from_lexical_with_options::(b"1.234e5", &OPTIONS); +//! assert_eq!(value, Ok(1.234e5)); +//! +//! let value = f64::from_lexical_with_options::(b"1.234E5", &OPTIONS); +//! assert_eq!(value, Err(Error::InvalidDigit(5))); +//! # } +//! ``` +//! +//! Enabling the [`format`](crate#format) API significantly increases compile +//! times, however, it enables a large amount of customization in how floats are +//! written. +//! +//! #### power-of-two +//! +//! Enable parsing numbers with radixes that are powers of two, that is, `2`, +//! `4`, `8`, `16`, and `32`. +//! +//! ```rust +//! # #[cfg(feature = "power-of-two")] { +//! use lexical_parse_float::{NumberFormatBuilder, Options, FromLexicalWithOptions}; +//! +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! const OPTIONS: Options = Options::new(); +//! let value = "1.0011101111100111011011001000101101000011100101011"; +//! let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Ok(1.234f64)); +//! # } +//! ``` +//! +//! #### radix +//! +//! Enable parsing numbers using all radixes from `2` to `36`. This requires +//! more static storage than [`power-of-two`][crate#power-of-two], and increases +//! compile times, but can be quite useful for esoteric programming languages +//! which use duodecimal floats, for example. +//! +//! ```rust +//! # #[cfg(feature = "radix")] { +//! use lexical_parse_float::{NumberFormatBuilder, Options, FromLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12); +//! const OPTIONS: Options = Options::new(); +//! let value = "1.29842830A44BAA2"; +//! let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Ok(1.234f64)); +//! # } +//! ``` +//! +//! #### compact +//! +//! Reduce the generated code size at the cost of performance. This minimizes +//! the number of static tables, inlining, and generics used, drastically +//! reducing the size of the generated binaries. However, this resulting +//! performance of the generated code is much lower. +//! +//! #### f16 +//! +//! This enables the use of the half-precision floats [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`]. However, since these have limited hardware support +//! and are primarily used for vectorized operations, they are parsed as if +//! they were an [`f32`]. Due to the low precision of 16-bit floats, the results +//! may appear to have significant rounding error. +//! +//! ```rust +//! # #[cfg(feature = "f16")] { +//! # use core::str; +//! use lexical_parse_float::{f16, FromLexical}; +//! +//! let value = "1.234375"; +//! let result = f16::from_lexical(value.as_bytes()); +//! assert_eq!(result, Ok(f16::from_f64_const(1.234f64))); +//! # } +//! ``` +//! +//! #### std +//! +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. +//! +//! # Comprehensive Configuration +//! +//! `lexical-parse-float` provides two main levels of configuration: +//! - The [`NumberFormatBuilder`], creating a packed struct with custom +//! formatting options. +//! - The [`Options`] API. +//! +//! ## Number Format +//! +//! The number format class provides numerous flags to specify number writing. +//! When the [`power-of-two`](#power-of-two) feature is enabled, additional +//! flags are added: +//! - The radix for the significant digits (default `10`). +//! - The radix for the exponent base (default `10`). +//! - The radix for the exponent digits (default `10`). +//! +//! When the [`format`](#format) feature is enabled, numerous other syntax and +//! digit separator flags are enabled, including: +//! - A digit separator character, to group digits for increased legibility. +//! - Whether leading, trailing, internal, and consecutive digit separators are +//! allowed. +//! - Toggling required float components, such as digits before the decimal +//! point. +//! - Toggling whether special floats are allowed or are case-sensitive. +//! +//! Many pre-defined constants therefore exist to simplify common use-cases, +//! including: +//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, +//! and many more. +//! +//! For a list of all supported fields, see [Parse +//! Float Fields][NumberFormatBuilder#parse-float-fields]. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`JSON`]: format::JSON +[`XML`]: format::XML +[`TOML`]: format::TOML +[`YAML`]: format::YAML +[`SQLite`]: format::SQLITE +[`Rust`]: format::RUST_LITERAL +[`Python`]: format::PYTHON_LITERAL +[`C#`]: format::CSHARP_LITERAL +[`FORTRAN`]: format::FORTRAN_LITERAL +[`COBOL`]: format::COBOL_LITERAL +" +)] +#![cfg_attr( + not(feature = "format"), + doc = " +[`JSON`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical-parse-float/latest/lexical_parse_float/format/constant.COBOL_LITERAL.html +" +)] +//! +//! ## Options API +//! +//! The Options API provides high-level options to specify number parsing +//! or writing, options not intrinsically tied to a number format. +//! For example, the Options API provides: +//! - The [`exponent`][OptionsBuilder::exponent] character (defaults to `b'e'` +//! or `b'^'`, depending on the radix). +//! - The [`decimal point`][OptionsBuilder::decimal_point] character (defaults +//! to `b'.'`). +//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string +//! [`representations`][Options::nan_string]. +//! +//! +//! In addition, pre-defined constants for each category of options may +//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`]. +//! +//! [`JSON-OPTS`]: options::JSON +//! +//! ## Examples +//! +//! An example of creating your own options to parse European-style +//! numbers (which use commas as decimal points, and periods as digit +//! separators) is as follows: +//! +//! ``` +//! # #[cfg(feature = "format")] { +//! # use core::num; +//! use lexical_parse_float::{format, FromLexicalWithOptions, NumberFormatBuilder, Options}; +//! +//! // This creates a format to parse a European-style float number. +//! // The decimal point is a comma, and the digit separators (optional) +//! // are periods. +//! const EUROPEAN: u128 = NumberFormatBuilder::new() +//! .digit_separator(num::NonZeroU8::new(b'.')) +//! .build_strict(); +//! const COMMA_OPTIONS: Options = Options::builder() +//! .decimal_point(b',') +//! .build_strict(); +//! assert_eq!( +//! f32::from_lexical_with_options::(b"300,10", &COMMA_OPTIONS), +//! Ok(300.10) +//! ); +//! +//! // Another example, using a pre-defined constant for JSON. +//! const JSON: u128 = format::JSON; +//! const JSON_OPTIONS: Options = Options::new(); +//! assert_eq!( +//! f32::from_lexical_with_options::(b"0e1", &JSON_OPTIONS), +//! Ok(0.0) +//! ); +//! assert_eq!( +//! f32::from_lexical_with_options::(b"1E+2", &JSON_OPTIONS), +//! Ok(100.0) +//! ); +//! # } +//! ``` +//! +//! # Higher-Level APIs +//! +//! If you would like an API that supports multiple numeric conversions rather +//! than just writing integers, use [`lexical`] or [`lexical-core`] instead. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! +//! # Algorithm +//! //! The default implementations are highly optimized both for simple //! strings, as well as input with large numbers of digits. In order to //! keep performance optimal for simple strings, we avoid overly branching @@ -23,30 +446,7 @@ //! we fallback to using optimized, big-integer algorithms, which are //! described in [Algorithm Approach](#algorithm-approach) below. //! -//! # Features -//! -//! * `std` - Use the standard library. -//! * `power-of-two` - Add support for parsing power-of-two integer strings. -//! * `radix` - Add support for strings of any radix. -//! * `format` - Add support for parsing custom integer formats. -//! * `compact` - Reduce code size at the cost of performance. -//! -//! # Note -//! -//! Only documented functionality is considered part of the public API: -//! any of the modules, internal functions, or structs may change -//! release-to-release without major or minor version changes. Use -//! internal implementation details at your own risk. -//! -//! lexical-parse-float mainly exists as an implementation detail for -//! lexical-core, although its API is stable. If you would like to use -//! a high-level API that writes to and parses from `String` and `&str`, -//! respectively, please look at [lexical](https://crates.io/crates/lexical) -//! instead. If you would like an API that supports multiple numeric -//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) -//! instead. -//! -//! # Machine Float-Only Algorithm +//! ## Machine Float-Only Algorithm //! //! We also support an algorithm that uses only machine floats for the //! fast-path algorithm, however, this may be slower for floats with large @@ -58,17 +458,34 @@ //! use lexical_parse_float::format::STANDARD; //! use lexical_parse_float::parse::ParseFloat; //! -//! let options = Options::new(); -//! let result = f64::fast_path_complete::<{ STANDARD }>(b"1.34000", &options); +//! const OPTIONS: Options = Options::new(); +//! let result = f64::fast_path_complete::<{ STANDARD }>(b"1.34000", &OPTIONS); //! assert_eq!(result, Ok(1.34000)); //! ``` //! -//! # Version Support +//! # Design +//! +//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) +//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md) +//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! - [Big Integer Implementation](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) +//! +//! # Safety Guarantees //! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. +//!
+//! //! -//! # Safety +//! This module uses some unsafe code to achieve accept acceptable performance. +//! The safety guarantees and logic are described below. +//! +//!
//! //! The primary use of unsafe code is in the big integer implementation, which //! for performance reasons requires unchecked indexing at certain points, where @@ -81,16 +498,10 @@ //! unsafe code are indexing a buffer where the index is proven to be within //! bounds within a few lines of code of the unsafe index. //! -//! # Design -//! -//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) -//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md) -//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) -//! - [Big Integer Implementation](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/BigInteger.md) -//! //! [hi]: //! [longbits]: //! [push_unchecked]: +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html // FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below // Clippy reasons were stabilized in 1.81.0. @@ -100,6 +511,8 @@ #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -162,7 +575,7 @@ pub use lexical_util::bf16::bf16; pub use lexical_util::error::Error; #[cfg(feature = "f16")] pub use lexical_util::f16::f16; -pub use lexical_util::format::{self, NumberFormatBuilder}; +pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder}; pub use lexical_util::options::ParseOptions; pub use lexical_util::result::Result; diff --git a/lexical-parse-float/src/mask.rs b/lexical-parse-float/src/mask.rs index a8362505..87b275ca 100644 --- a/lexical-parse-float/src/mask.rs +++ b/lexical-parse-float/src/mask.rs @@ -8,9 +8,7 @@ /// /// ```rust /// # use lexical_parse_float::mask::lower_n_mask; -/// # pub fn main() { /// assert_eq!(lower_n_mask(2), 0b11); -/// # } /// ``` #[must_use] #[inline(always)] @@ -30,9 +28,7 @@ pub const fn lower_n_mask(n: u64) -> u64 { /// /// ```rust /// # use lexical_parse_float::mask::lower_n_halfway; -/// # pub fn main() { /// assert_eq!(lower_n_halfway(2), 0b10); -/// # } /// ``` #[must_use] #[inline(always)] @@ -52,9 +48,7 @@ pub const fn lower_n_halfway(n: u64) -> u64 { /// /// ```rust /// # use lexical_parse_float::mask::nth_bit; -/// # pub fn main() { /// assert_eq!(nth_bit(2), 0b100); -/// # } /// ``` #[must_use] #[inline(always)] diff --git a/lexical-parse-float/src/options.rs b/lexical-parse-float/src/options.rs index 509bba16..f2253529 100644 --- a/lexical-parse-float/src/options.rs +++ b/lexical-parse-float/src/options.rs @@ -1,4 +1,189 @@ //! Configuration options for parsing floats. +//! +//! This enables extensive control over how the float is parsed, from +//! control characters like the decimal point and the valid non-finite +//! float representations. +//! +//! # Examples +//! +//! For example, to parse a European-style float: +//! +//! ```rust +//! use lexical_parse_float::{FromLexicalWithOptions, Options}; +//! use lexical_parse_float::format::STANDARD; +//! +//! const OPTIONS: Options = Options::builder() +//! .decimal_point(b',') +//! .build_strict(); +//! let value = "1,2345e300"; +//! let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Ok(1.2345e300)); +//! ``` +//! +//! # Pre-Defined Formats +//! +//! These are the pre-defined formats for parsing numbers from various +//! programming, markup, and data languages. +//! +//! - [`STANDARD`]: Standard number format. +//! - [`DECIMAL_COMMA`]: Numerical format with a decimal comma. +//! - [`HEX_FLOAT`]: Numerical format for hexadecimal floats, which use a `p` +//! exponent. +//! - [`RUST_LITERAL`]: Number format for a [`Rust`] literal floating-point +//! number. +//! - [`PYTHON_LITERAL`]: Number format for a [`Python`] literal floating-point +//! number. +//! - [`CXX_LITERAL`]: Number format for a [`C++`] literal floating-point +//! number. +//! - [`C_LITERAL`]: Number format for a [`C`] literal floating-point number. +//! - [`RUBY_LITERAL`]: Number format for a [`Ruby`] literal floating-point +//! number. +//! - [`RUBY_STRING`]: Number format to parse a [`Ruby`] float from string. +//! - [`SWIFT_LITERAL`]: Number format for a [`Swift`] literal floating-point +//! number. +//! - [`GO_LITERAL`]: Number format for a [`Golang`] literal floating-point +//! number. +//! - [`HASKELL_LITERAL`]: Number format for a [`Haskell`] literal +//! floating-point number. +//! - [`HASKELL_STRING`]: Number format to parse a [`Haskell`] float from +//! string. +//! - [`JAVASCRIPT_LITERAL`]: Number format for a [`Javascript`] literal +//! floating-point number. +//! - [`JAVASCRIPT_STRING`]: Number format to parse a [`Javascript`] float from +//! string. +//! - [`PERL_LITERAL`]: Number format for a [`Perl`] literal floating-point +//! number. +//! - [`PHP_LITERAL`]: Number format for a [`PHP`] literal floating-point +//! number. +//! - [`JAVA_LITERAL`]: Number format for a [`Java`] literal floating-point +//! number. +//! - [`JAVA_STRING`]: Number format to parse a [`Java`] float from string. +//! - [`R_LITERAL`]: Number format for an [`R`] literal floating-point number. +//! - [`KOTLIN_LITERAL`]: Number format for a [`Kotlin`] literal floating-point +//! number. +//! - [`KOTLIN_STRING`]: Number format to parse a [`Kotlin`] float from string. +//! - [`JULIA_LITERAL`]: Number format for a [`Julia`] literal floating-point +//! number. +//! - [`CSHARP_LITERAL`]: Number format for a [`C#`] literal floating-point +//! number. +//! - [`CSHARP_STRING`]: Number format to parse a [`C#`] float from string. +//! - [`KAWA_LITERAL`]: Number format for a [`Kawa`] literal floating-point +//! number. +//! - [`KAWA_STRING`]: Number format to parse a [`Kawa`] float from string. +//! - [`GAMBITC_LITERAL`]: Number format for a [`Gambit-C`] literal +//! floating-point number. +//! - [`GAMBITC_STRING`]: Number format to parse a [`Gambit-C`] float from +//! string. +//! - [`GUILE_LITERAL`]: Number format for a [`Guile`] literal floating-point +//! number. +//! - [`GUILE_STRING`]: Number format to parse a [`Guile`] float from string. +//! - [`CLOJURE_LITERAL`]: Number format for a [`Clojure`] literal +//! floating-point number. +//! - [`CLOJURE_STRING`]: Number format to parse a [`Clojure`] float from +//! string. +//! - [`ERLANG_LITERAL`]: Number format for an [`Erlang`] literal floating-point +//! number. +//! - [`ERLANG_STRING`]: Number format to parse an [`Erlang`] float from string. +//! - [`ELM_LITERAL`]: Number format for an [`Elm`] literal floating-point +//! number. +//! - [`ELM_STRING`]: Number format to parse an [`Elm`] float from string. +//! - [`SCALA_LITERAL`]: Number format for a [`Scala`] literal floating-point +//! number. +//! - [`SCALA_STRING`]: Number format to parse a [`Scala`] float from string. +//! - [`ELIXIR_LITERAL`]: Number format for an [`Elixir`] literal floating-point +//! number. +//! - [`ELIXIR_STRING`]: Number format to parse an [`Elixir`] float from string. +//! - [`FORTRAN_LITERAL`]: Number format for a [`FORTRAN`] literal +//! floating-point number. +//! - [`D_LITERAL`]: Number format for a [`D`] literal floating-point number. +//! - [`COFFEESCRIPT_LITERAL`]: Number format for a [`Coffeescript`] literal +//! floating-point number. +//! - [`COFFEESCRIPT_STRING`]: Number format to parse a [`Coffeescript`] float +//! from string. +//! - [`COBOL_LITERAL`]: Number format for a [`COBOL`] literal floating-point +//! number. +//! - [`COBOL_STRING`]: Number format to parse a [`COBOL`] float from string. +//! - [`FSHARP_LITERAL`]: Number format for an [`F#`] literal floating-point +//! number. +//! - [`VB_LITERAL`]: Number format for a [`Visual Basic`] literal +//! floating-point number. +//! - [`VB_STRING`]: Number format to parse a [`Visual Basic`] float from +//! string. +//! - [`OCAML_LITERAL`]: Number format for an [`OCaml`] literal floating-point +//! number. +//! - [`OBJECTIVEC_LITERAL`]: Number format for an [`Objective-C`] literal +//! floating-point number. +//! - [`OBJECTIVEC_STRING`]: Number format to parse an [`Objective-C`] float +//! from string. +//! - [`REASONML_LITERAL`]: Number format for an [`ReasonML`] literal +//! floating-point number. +//! - [`MATLAB_LITERAL`]: Number format for a [`MATLAB`] literal floating-point +//! number. +//! - [`ZIG_LITERAL`]: Number format for a [`Zig`] literal floating-point +//! number. +//! - [`SAGE_LITERAL`]: Number format for a [`Sage`] literal floating-point +//! number. +//! - [`JSON`]: Number format for a [`JSON`][`JSON-REF`] literal floating-point +//! number. +//! - [`TOML`]: Number format for a [`TOML`][`TOML-REF`] literal floating-point +//! number. +//! - [`YAML`]: Number format for a [`YAML`][`YAML-REF`] literal floating-point +//! number. +//! - [`XML`]: Number format for an [`XML`][`XML-REF`] literal floating-point +//! number. +//! - [`SQLITE`]: Number format for a [`SQLite`] literal floating-point number. +//! - [`POSTGRESQL`]: Number format for a [`PostgreSQL`] literal floating-point +//! number. +//! - [`MYSQL`]: Number format for a [`MySQL`] literal floating-point number. +//! - [`MONGODB`]: Number format for a [`MongoDB`] literal floating-point +//! number. +//! +//! +//! +//! [`Rust`]: https://www.rust-lang.org/ +//! [`Python`]: https://www.python.org/ +//! [`C++`]: https://en.cppreference.com/w/ +//! [`C`]: https://en.cppreference.com/w/c +//! [`Ruby`]: https://www.ruby-lang.org/en/ +//! [`Swift`]: https://developer.apple.com/swift/ +//! [`Golang`]: https://go.dev/ +//! [`Haskell`]: https://www.haskell.org/ +//! [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +//! [`Perl`]: https://www.perl.org/ +//! [`PHP`]: https://www.php.net/ +//! [`Java`]: https://www.java.com/en/ +//! [`R`]: https://www.r-project.org/ +//! [`Kotlin`]: https://kotlinlang.org/ +//! [`Julia`]: https://julialang.org/ +//! [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +//! [`Kawa`]: https://www.gnu.org/software/kawa/ +//! [`Gambit-C`]: https://gambitscheme.org/ +//! [`Guile`]: https://www.gnu.org/software/guile/ +//! [`Clojure`]: https://clojure.org/ +//! [`Erlang`]: https://www.erlang.org/ +//! [`Elm`]: https://elm-lang.org/ +//! [`Scala`]: https://www.scala-lang.org/ +//! [`Elixir`]: https://elixir-lang.org/ +//! [`FORTRAN`]: https://fortran-lang.org/ +//! [`D`]: https://dlang.org/ +//! [`Coffeescript`]: https://coffeescript.org/ +//! [`Cobol`]: https://www.ibm.com/think/topics/cobol +//! [`F#`]: https://fsharp.org/ +//! [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +//! [`OCaml`]: https://ocaml.org/ +//! [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +//! [`ReasonML`]: https://reasonml.github.io/ +//! [`Matlab`]: https://www.mathworks.com/products/matlab.html +//! [`Zig`]: https://ziglang.org/ +//! [`Sage`]: https://www.sagemath.org/ +//! [`JSON-REF`]: https://www.json.org/json-en.html +//! [`TOML-REF`]: https://toml.io/en/ +//! [`YAML-REF`]: https://yaml.org/ +//! [`XML-REF`]: https://en.wikipedia.org/wiki/XML +//! [`SQLite`]: https://www.sqlite.org/ +//! [`PostgreSQL`]: https://www.postgresql.org/ +//! [`MySQL`]: https://www.mysql.com/ +//! [`MongoDB`]: https://www.mongodb.com/ #![allow(clippy::must_use_candidate)] @@ -6,12 +191,29 @@ use lexical_util::ascii::{is_valid_ascii, is_valid_letter_slice}; use lexical_util::error::Error; use lexical_util::options::{self, ParseOptions}; use lexical_util::result::Result; -use static_assertions::const_assert; /// Maximum length for a special string. pub const MAX_SPECIAL_STRING_LENGTH: usize = 50; -/// Builder for `Options`. +/// Builder for [`Options`]. +/// +/// This enables extensive control over how the float is parsed, from +/// control characters like the decimal point and the valid non-finite +/// float representations. +/// +/// # Examples +/// +/// ```rust +/// use lexical_parse_float::{FromLexicalWithOptions, Options}; +/// use lexical_parse_float::format::STANDARD; +/// +/// const OPTIONS: Options = Options::builder() +/// .decimal_point(b',') +/// .build_strict(); +/// let value = "1,2345e300"; +/// let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +/// assert_eq!(result, Ok(1.2345e300)); +/// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OptionsBuilder { /// Disable the use of arbitrary-precision arithmetic, and always @@ -46,36 +248,107 @@ impl OptionsBuilder { // GETTERS /// Get if we disable the use of arbitrary-precision arithmetic. + /// + /// Lossy algorithms never use the fallback, slow algorithm. Defaults to + /// [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::builder().get_lossy(), false); + /// ``` #[inline(always)] pub const fn get_lossy(&self) -> bool { self.lossy } /// Get the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::builder().get_exponent(), b'e'); + /// ``` #[inline(always)] pub const fn get_exponent(&self) -> u8 { self.exponent } /// Get the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::builder().get_decimal_point(), b'.'); + /// ``` #[inline(always)] pub const fn get_decimal_point(&self) -> u8 { self.decimal_point } /// Get the string representation for `NaN`. + /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_nan_string(), Some("NaN".as_bytes())); + /// ``` #[inline(always)] pub const fn get_nan_string(&self) -> Option<&'static [u8]> { self.nan_string } /// Get the short string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_inf_string(), Some("inf".as_bytes())); + /// ``` #[inline(always)] pub const fn get_inf_string(&self) -> Option<&'static [u8]> { self.inf_string } /// Get the long string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). Defaults to `infinity`. + /// + /// [`get_inf_string`]: Self::get_inf_string + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_infinity_string(), Some("infinity".as_bytes())); + /// ``` #[inline(always)] pub const fn get_infinity_string(&self) -> Option<&'static [u8]> { self.infinity_string @@ -84,6 +357,20 @@ impl OptionsBuilder { // SETTERS /// Set if we disable the use of arbitrary-precision arithmetic. + /// + /// Lossy algorithms never use the fallback, slow algorithm. Defaults to + /// [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .lossy(true) + /// .build_strict(); + /// assert_eq!(OPTIONS.lossy(), true); + /// ``` #[must_use] #[inline(always)] pub const fn lossy(mut self, lossy: bool) -> Self { @@ -92,6 +379,20 @@ impl OptionsBuilder { } /// Set the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .exponent(b'^') + /// .build_strict(); + /// assert_eq!(OPTIONS.exponent(), b'^'); + /// ``` #[must_use] #[inline(always)] pub const fn exponent(mut self, exponent: u8) -> Self { @@ -100,6 +401,20 @@ impl OptionsBuilder { } /// Set the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .exponent(b',') + /// .build_strict(); + /// assert_eq!(OPTIONS.exponent(), b','); + /// ``` #[must_use] #[inline(always)] pub const fn decimal_point(mut self, decimal_point: u8) -> Self { @@ -108,6 +423,30 @@ impl OptionsBuilder { } /// Set the string representation for `NaN`. + /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`NaN`][f64::NAN] returns an error. Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .nan_string(Some(b"nan")) + /// .build_strict(); + /// assert_eq!(OPTIONS.nan_string(), Some(b"nan".as_ref())); + /// ``` + /// + /// Panics + /// + /// Setting a value with more than 50 elements will panic at runtime. You + /// should always build the format using [`build_strict`] or checking + /// [`is_valid`] prior to using the format, to avoid unexpected panics. + /// + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid #[must_use] #[inline(always)] pub const fn nan_string(mut self, nan_string: Option<&'static [u8]>) -> Self { @@ -116,6 +455,32 @@ impl OptionsBuilder { } /// Set the short string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .inf_string(Some(b"Infinity")) + /// .build_strict(); + /// assert_eq!(OPTIONS.inf_string(), Some(b"Infinity".as_ref())); + /// ``` + /// + /// Panics + /// + /// Setting a value with more than 50 elements or one that is longer than + /// [`infinity_string`] will panic at runtime. You should always build + /// the format using [`build_strict`] or checking [`is_valid`] prior to + /// using the format, to avoid unexpected panics. + /// + /// [`infinity_string`]: Self::infinity_string + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid #[must_use] #[inline(always)] pub const fn inf_string(mut self, inf_string: Option<&'static [u8]>) -> Self { @@ -124,6 +489,32 @@ impl OptionsBuilder { } /// Set the long string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `infinity`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .infinity_string(Some(b"Infinity")) + /// .build_strict(); + /// assert_eq!(OPTIONS.infinity_string(), Some(b"Infinity".as_ref())); + /// ``` + /// + /// Panics + /// + /// Setting a value with more than 50 elements or one that is shorter than + /// [`inf_string`] will panic at runtime. You should always build the format + /// using [`build_strict`] or checking [`is_valid`] prior to + /// using the format, to avoid unexpected panics. + /// + /// [`inf_string`]: Self::inf_string + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid #[must_use] #[inline(always)] pub const fn infinity_string(mut self, infinity_string: Option<&'static [u8]>) -> Self { @@ -133,7 +524,10 @@ impl OptionsBuilder { // BUILDERS - /// Determine if `nan_str` is valid. + /// Determine if [`nan_string`] is valid. + /// + /// [`nan_string`]: Self::nan_string + #[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else, clippy::needless_bool)] // reason = "more idiomatic" pub const fn nan_str_is_valid(&self) -> bool { @@ -154,7 +548,10 @@ impl OptionsBuilder { } } - /// Determine if `inf_str` is valid. + /// Determine if [`inf_string`] is valid. + /// + /// [`inf_string`]: Self::inf_string + #[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else, clippy::needless_bool)] // reason = "more idiomatic" pub const fn inf_str_is_valid(&self) -> bool { @@ -180,7 +577,10 @@ impl OptionsBuilder { } } - /// Determine if `infinity_string` is valid. + /// Determine if [`infinity_string`] is valid. + /// + /// [`infinity_string`]: Self::infinity_string + #[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else, clippy::needless_bool)] // reason = "more idiomatic" pub const fn infinity_string_is_valid(&self) -> bool { @@ -224,14 +624,19 @@ impl OptionsBuilder { } } - /// Build the Options struct without validation. + /// Build the [`Options`] struct without validation. /// /// # Panics /// /// This is completely safe, however, misusing this, especially - /// the `nan_string`, `inf_string`, and `infinity_string` could - /// panic at runtime. Always use [`MAX_SPECIAL_STRING_LENGTH`] and - /// check if [`Self::is_valid`] prior to using a created format string. + /// the [`nan_string`], [`inf_string`], and [`infinity_string`] could + /// panic at runtime. Always use [`is_valid`] prior to using the built + /// options. + /// + /// [`inf_string`]: Self::inf_string + /// [`nan_string`]: Self::nan_string + /// [`infinity_string`]: Self::infinity_string + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn build_unchecked(&self) -> Options { Options { @@ -244,7 +649,20 @@ impl OptionsBuilder { } } - /// Build the Options struct. + /// Build the [`Options`] struct, panicking if the builder is invalid. + /// + /// # Panics + /// + /// If the built options are not valid. + #[inline(always)] + pub const fn build_strict(&self) -> Options { + match self.build() { + Ok(value) => value, + Err(error) => core::panic!("{}", error.description()), + } + } + + /// Build the [`Options`] struct. /// /// # Errors /// @@ -312,20 +730,30 @@ impl Default for OptionsBuilder { /// Options to customize parsing floats. /// +/// This enables extensive control over how the float is parsed, from +/// control characters like the decimal point and the valid non-finite +/// float representations. +/// /// # Examples /// /// ```rust -/// use lexical_parse_float::Options; +/// use lexical_parse_float::{FromLexicalWithOptions, Options}; +/// use lexical_parse_float::format::STANDARD; /// -/// # pub fn main() { -/// let options = Options::builder() +/// const OPTIONS: Options = Options::builder() /// .lossy(true) /// .nan_string(Some(b"NaN")) /// .inf_string(Some(b"Inf")) /// .infinity_string(Some(b"Infinity")) -/// .build() -/// .unwrap(); -/// # } +/// .build_strict(); +/// +/// let value = "1.2345e300"; +/// let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +/// assert_eq!(result, Ok(1.2345e300)); +/// +/// let value = "NaN"; +/// let result = f64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +/// assert_eq!(result.map(|x| x.is_nan()), Ok(true)); /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Options { @@ -354,6 +782,9 @@ impl Options { } /// Create the default options for a given radix. + /// + /// This sets the exponent to `^` for any radix where `e` + /// would be a valid digit. #[inline(always)] #[cfg(feature = "power-of-two")] pub const fn from_radix(radix: u8) -> Self { @@ -376,36 +807,105 @@ impl Options { } /// Get if we disable the use of arbitrary-precision arithmetic. + /// + /// Lossy algorithms never use the fallback, slow algorithm. Defaults to + /// [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().lossy(), false); + /// ``` #[inline(always)] pub const fn lossy(&self) -> bool { self.lossy } /// Get the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().exponent(), b'e'); + /// ``` #[inline(always)] pub const fn exponent(&self) -> u8 { self.exponent } /// Get the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().decimal_point(), b'.'); + /// ``` #[inline(always)] pub const fn decimal_point(&self) -> u8 { self.decimal_point } /// Get the string representation for `NaN`. + /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`NaN`][f64::NAN] returns an error. Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().nan_string(), Some(b"NaN".as_ref())); + /// ``` #[inline(always)] pub const fn nan_string(&self) -> Option<&'static [u8]> { self.nan_string } /// Get the short string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().inf_string(), Some(b"inf".as_ref())); + /// ``` #[inline(always)] pub const fn inf_string(&self) -> Option<&'static [u8]> { self.inf_string } /// Get the long string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then parsing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `infinity`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_float::options::Options; + /// + /// assert_eq!(Options::new().infinity_string(), Some(b"infinity".as_ref())); + /// ``` #[inline(always)] pub const fn infinity_string(&self) -> Option<&'static [u8]> { self.infinity_string @@ -415,50 +915,56 @@ impl Options { /// Set if we disable the use of arbitrary-precision arithmetic. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_lossy(&mut self, lossy: bool) { self.lossy = lossy; } /// Set the character to designate the exponent component of a float. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_exponent(&mut self, exponent: u8) { self.exponent = exponent; } /// Set the character to separate the integer from the fraction components. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_decimal_point(&mut self, decimal_point: u8) { self.decimal_point = decimal_point; } /// Set the string representation for `NaN`. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_nan_string(&mut self, nan_string: Option<&'static [u8]>) { self.nan_string = nan_string; } /// Set the short string representation for `Infinity` #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_inf_string(&mut self, inf_string: Option<&'static [u8]>) { self.inf_string = inf_string; } /// Set the long string representation for `Infinity` #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_infinity_string(&mut self, infinity_string: Option<&'static [u8]>) { self.infinity_string = infinity_string; } // BUILDERS - /// Get `OptionsBuilder` as a static function. + /// Get [`OptionsBuilder`] as a static function. #[must_use] #[inline(always)] pub const fn builder() -> OptionsBuilder { OptionsBuilder::new() } - /// Create `OptionsBuilder` using existing values. + /// Create [`OptionsBuilder`] using existing values. #[must_use] #[inline(always)] pub const fn rebuild(&self) -> OptionsBuilder { @@ -504,518 +1010,458 @@ const fn unwrap_str(option: Option<&'static [u8]>) -> &'static [u8] { /// Standard number format. #[rustfmt::skip] pub const STANDARD: Options = Options::new(); -const_assert!(STANDARD.is_valid()); /// Numerical format with a decimal comma. /// This is the standard numerical format for most of the world. #[rustfmt::skip] pub const DECIMAL_COMMA: Options = Options::builder() - .decimal_point(b',') - .build_unchecked(); -const_assert!(DECIMAL_COMMA.is_valid()); + .decimal_point(b',') + .build_strict(); /// Numerical format for hexadecimal floats, which use a `p` exponent. #[rustfmt::skip] pub const HEX_FLOAT: Options = Options::builder() - .exponent(b'p') - .build_unchecked(); -const_assert!(HEX_FLOAT.is_valid()); + .exponent(b'p') + .build_strict(); /// Numerical format where `^` is used as the exponent notation character. /// This isn't very common, but is useful when `e` or `p` are valid digits. #[rustfmt::skip] pub const CARAT_EXPONENT: Options = Options::builder() - .exponent(b'^') - .build_unchecked(); -const_assert!(CARAT_EXPONENT.is_valid()); + .exponent(b'^') + .build_strict(); /// Number format for a `Rust` literal floating-point number. #[rustfmt::skip] pub const RUST_LITERAL: Options = Options::builder() - .nan_string(options::RUST_LITERAL) - .inf_string(options::RUST_LITERAL) - .infinity_string(options::RUST_LITERAL) - .build_unchecked(); -const_assert!(RUST_LITERAL.is_valid()); + .nan_string(options::RUST_LITERAL) + .inf_string(options::RUST_LITERAL) + .infinity_string(options::RUST_LITERAL) + .build_strict(); /// Number format for a `Python` literal floating-point number. #[rustfmt::skip] pub const PYTHON_LITERAL: Options = Options::builder() - .nan_string(options::PYTHON_LITERAL) - .inf_string(options::PYTHON_LITERAL) - .infinity_string(options::PYTHON_LITERAL) - .build_unchecked(); -const_assert!(PYTHON_LITERAL.is_valid()); + .nan_string(options::PYTHON_LITERAL) + .inf_string(options::PYTHON_LITERAL) + .infinity_string(options::PYTHON_LITERAL) + .build_strict(); /// Number format for a `C++` literal floating-point number. #[rustfmt::skip] pub const CXX_LITERAL: Options = Options::builder() - .nan_string(options::CXX_LITERAL_NAN) - .inf_string(options::CXX_LITERAL_INF) - .infinity_string(options::CXX_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(CXX_LITERAL.is_valid()); + .nan_string(options::CXX_LITERAL_NAN) + .inf_string(options::CXX_LITERAL_INF) + .infinity_string(options::CXX_LITERAL_INFINITY) + .build_strict(); /// Number format for a `C` literal floating-point number. #[rustfmt::skip] pub const C_LITERAL: Options = Options::builder() - .nan_string(options::C_LITERAL_NAN) - .inf_string(options::C_LITERAL_INF) - .infinity_string(options::C_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(CXX_LITERAL.is_valid()); + .nan_string(options::C_LITERAL_NAN) + .inf_string(options::C_LITERAL_INF) + .infinity_string(options::C_LITERAL_INFINITY) + .build_strict(); /// Number format for a `Ruby` literal floating-point number. #[rustfmt::skip] pub const RUBY_LITERAL: Options = Options::builder() - .nan_string(options::RUBY_LITERAL_NAN) - .inf_string(options::RUBY_LITERAL_INF) - .infinity_string(options::RUBY_LITERAL_INF) - .build_unchecked(); -const_assert!(RUBY_LITERAL.is_valid()); + .nan_string(options::RUBY_LITERAL_NAN) + .inf_string(options::RUBY_LITERAL_INF) + .infinity_string(options::RUBY_LITERAL_INF) + .build_strict(); /// Number format to parse a `Ruby` float from string. /// `Ruby` can write NaN and Infinity as strings, but won't round-trip them back to floats. #[rustfmt::skip] pub const RUBY_STRING: Options = Options::builder() - .nan_string(options::RUBY_STRING_NONE) - .inf_string(options::RUBY_STRING_NONE) - .infinity_string(options::RUBY_STRING_NONE) - .build_unchecked(); -const_assert!(RUBY_STRING.is_valid()); + .nan_string(options::RUBY_STRING_NONE) + .inf_string(options::RUBY_STRING_NONE) + .infinity_string(options::RUBY_STRING_NONE) + .build_strict(); /// Number format for a `Swift` literal floating-point number. #[rustfmt::skip] pub const SWIFT_LITERAL: Options = Options::builder() - .nan_string(options::SWIFT_LITERAL) - .inf_string(options::SWIFT_LITERAL) - .infinity_string(options::SWIFT_LITERAL) - .build_unchecked(); -const_assert!(SWIFT_LITERAL.is_valid()); + .nan_string(options::SWIFT_LITERAL) + .inf_string(options::SWIFT_LITERAL) + .infinity_string(options::SWIFT_LITERAL) + .build_strict(); /// Number format for a `Go` literal floating-point number. #[rustfmt::skip] pub const GO_LITERAL: Options = Options::builder() - .nan_string(options::GO_LITERAL) - .inf_string(options::GO_LITERAL) - .infinity_string(options::GO_LITERAL) - .build_unchecked(); -const_assert!(GO_LITERAL.is_valid()); + .nan_string(options::GO_LITERAL) + .inf_string(options::GO_LITERAL) + .infinity_string(options::GO_LITERAL) + .build_strict(); /// Number format for a `Haskell` literal floating-point number. #[rustfmt::skip] pub const HASKELL_LITERAL: Options = Options::builder() - .nan_string(options::HASKELL_LITERAL) - .inf_string(options::HASKELL_LITERAL) - .infinity_string(options::HASKELL_LITERAL) - .build_unchecked(); -const_assert!(HASKELL_LITERAL.is_valid()); + .nan_string(options::HASKELL_LITERAL) + .inf_string(options::HASKELL_LITERAL) + .infinity_string(options::HASKELL_LITERAL) + .build_strict(); /// Number format to parse a `Haskell` float from string. #[rustfmt::skip] pub const HASKELL_STRING: Options = Options::builder() - .inf_string(options::HASKELL_STRING_INF) - .infinity_string(options::HASKELL_STRING_INFINITY) - .build_unchecked(); -const_assert!(HASKELL_STRING.is_valid()); + .inf_string(options::HASKELL_STRING_INF) + .infinity_string(options::HASKELL_STRING_INFINITY) + .build_strict(); /// Number format for a `Javascript` literal floating-point number. #[rustfmt::skip] pub const JAVASCRIPT_LITERAL: Options = Options::builder() - .inf_string(options::JAVASCRIPT_INF) - .infinity_string(options::JAVASCRIPT_INFINITY) - .build_unchecked(); -const_assert!(JAVASCRIPT_LITERAL.is_valid()); + .inf_string(options::JAVASCRIPT_INF) + .infinity_string(options::JAVASCRIPT_INFINITY) + .build_strict(); /// Number format to parse a `Javascript` float from string. #[rustfmt::skip] pub const JAVASCRIPT_STRING: Options = Options::builder() - .inf_string(options::JAVASCRIPT_INF) - .infinity_string(options::JAVASCRIPT_INFINITY) - .build_unchecked(); -const_assert!(JAVASCRIPT_STRING.is_valid()); + .inf_string(options::JAVASCRIPT_INF) + .infinity_string(options::JAVASCRIPT_INFINITY) + .build_strict(); /// Number format for a `Perl` literal floating-point number. #[rustfmt::skip] pub const PERL_LITERAL: Options = Options::builder() - .nan_string(options::PERL_LITERAL) - .inf_string(options::PERL_LITERAL) - .infinity_string(options::PERL_LITERAL) - .build_unchecked(); -const_assert!(PERL_LITERAL.is_valid()); + .nan_string(options::PERL_LITERAL) + .inf_string(options::PERL_LITERAL) + .infinity_string(options::PERL_LITERAL) + .build_strict(); /// Number format for a `PHP` literal floating-point number. #[rustfmt::skip] pub const PHP_LITERAL: Options = Options::builder() - .nan_string(options::PHP_LITERAL_NAN) - .inf_string(options::PHP_LITERAL_INF) - .infinity_string(options::PHP_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(PHP_LITERAL.is_valid()); + .nan_string(options::PHP_LITERAL_NAN) + .inf_string(options::PHP_LITERAL_INF) + .infinity_string(options::PHP_LITERAL_INFINITY) + .build_strict(); /// Number format for a `Java` literal floating-point number. #[rustfmt::skip] pub const JAVA_LITERAL: Options = Options::builder() - .nan_string(options::JAVA_LITERAL) - .inf_string(options::JAVA_LITERAL) - .infinity_string(options::JAVA_LITERAL) - .build_unchecked(); -const_assert!(JAVA_LITERAL.is_valid()); + .nan_string(options::JAVA_LITERAL) + .inf_string(options::JAVA_LITERAL) + .infinity_string(options::JAVA_LITERAL) + .build_strict(); /// Number format to parse a `Java` float from string. #[rustfmt::skip] pub const JAVA_STRING: Options = Options::builder() - .inf_string(options::JAVA_STRING_INF) - .infinity_string(options::JAVA_STRING_INFINITY) - .build_unchecked(); -const_assert!(JAVA_STRING.is_valid()); + .inf_string(options::JAVA_STRING_INF) + .infinity_string(options::JAVA_STRING_INFINITY) + .build_strict(); /// Number format for an `R` literal floating-point number. #[rustfmt::skip] pub const R_LITERAL: Options = Options::builder() - .inf_string(options::R_LITERAL_INF) - .infinity_string(options::R_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(R_LITERAL.is_valid()); + .inf_string(options::R_LITERAL_INF) + .infinity_string(options::R_LITERAL_INFINITY) + .build_strict(); /// Number format for a `Kotlin` literal floating-point number. #[rustfmt::skip] pub const KOTLIN_LITERAL: Options = Options::builder() - .nan_string(options::KOTLIN_LITERAL) - .inf_string(options::KOTLIN_LITERAL) - .infinity_string(options::KOTLIN_LITERAL) - .build_unchecked(); -const_assert!(KOTLIN_LITERAL.is_valid()); + .nan_string(options::KOTLIN_LITERAL) + .inf_string(options::KOTLIN_LITERAL) + .infinity_string(options::KOTLIN_LITERAL) + .build_strict(); /// Number format to parse a `Kotlin` float from string. #[rustfmt::skip] pub const KOTLIN_STRING: Options = Options::builder() - .inf_string(options::KOTLIN_STRING_INF) - .infinity_string(options::KOTLIN_STRING_INFINITY) - .build_unchecked(); -const_assert!(KOTLIN_STRING.is_valid()); + .inf_string(options::KOTLIN_STRING_INF) + .infinity_string(options::KOTLIN_STRING_INFINITY) + .build_strict(); /// Number format for a `Julia` literal floating-point number. #[rustfmt::skip] pub const JULIA_LITERAL: Options = Options::builder() - .inf_string(options::JULIA_LITERAL_INF) - .infinity_string(options::JULIA_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(JULIA_LITERAL.is_valid()); + .inf_string(options::JULIA_LITERAL_INF) + .infinity_string(options::JULIA_LITERAL_INFINITY) + .build_strict(); /// Number format for a `C#` literal floating-point number. #[rustfmt::skip] pub const CSHARP_LITERAL: Options = Options::builder() - .nan_string(options::CSHARP_LITERAL) - .inf_string(options::CSHARP_LITERAL) - .infinity_string(options::CSHARP_LITERAL) - .build_unchecked(); -const_assert!(CSHARP_LITERAL.is_valid()); + .nan_string(options::CSHARP_LITERAL) + .inf_string(options::CSHARP_LITERAL) + .infinity_string(options::CSHARP_LITERAL) + .build_strict(); /// Number format to parse a `C#` float from string. #[rustfmt::skip] pub const CSHARP_STRING: Options = Options::builder() - .inf_string(options::CSHARP_STRING_INF) - .infinity_string(options::CSHARP_STRING_INFINITY) - .build_unchecked(); -const_assert!(CSHARP_STRING.is_valid()); + .inf_string(options::CSHARP_STRING_INF) + .infinity_string(options::CSHARP_STRING_INFINITY) + .build_strict(); /// Number format for a `Kawa` literal floating-point number. #[rustfmt::skip] pub const KAWA_LITERAL: Options = Options::builder() - .nan_string(options::KAWA) - .inf_string(options::KAWA) - .infinity_string(options::KAWA) - .build_unchecked(); -const_assert!(KAWA_LITERAL.is_valid()); + .nan_string(options::KAWA) + .inf_string(options::KAWA) + .infinity_string(options::KAWA) + .build_strict(); /// Number format to parse a `Kawa` float from string. #[rustfmt::skip] pub const KAWA_STRING: Options = Options::builder() - .nan_string(options::KAWA) - .inf_string(options::KAWA) - .infinity_string(options::KAWA) - .build_unchecked(); -const_assert!(KAWA_STRING.is_valid()); + .nan_string(options::KAWA) + .inf_string(options::KAWA) + .infinity_string(options::KAWA) + .build_strict(); /// Number format for a `Gambit-C` literal floating-point number. #[rustfmt::skip] pub const GAMBITC_LITERAL: Options = Options::builder() - .nan_string(options::GAMBITC) - .inf_string(options::GAMBITC) - .infinity_string(options::GAMBITC) - .build_unchecked(); -const_assert!(GAMBITC_LITERAL.is_valid()); + .nan_string(options::GAMBITC) + .inf_string(options::GAMBITC) + .infinity_string(options::GAMBITC) + .build_strict(); /// Number format to parse a `Gambit-C` float from string. #[rustfmt::skip] pub const GAMBITC_STRING: Options = Options::builder() - .nan_string(options::GAMBITC) - .inf_string(options::GAMBITC) - .infinity_string(options::GAMBITC) - .build_unchecked(); -const_assert!(GAMBITC_STRING.is_valid()); + .nan_string(options::GAMBITC) + .inf_string(options::GAMBITC) + .infinity_string(options::GAMBITC) + .build_strict(); /// Number format for a `Guile` literal floating-point number. #[rustfmt::skip] pub const GUILE_LITERAL: Options = Options::builder() - .nan_string(options::GUILE) - .inf_string(options::GUILE) - .infinity_string(options::GUILE) - .build_unchecked(); -const_assert!(GUILE_LITERAL.is_valid()); + .nan_string(options::GUILE) + .inf_string(options::GUILE) + .infinity_string(options::GUILE) + .build_strict(); /// Number format to parse a `Guile` float from string. #[rustfmt::skip] pub const GUILE_STRING: Options = Options::builder() - .nan_string(options::GUILE) - .inf_string(options::GUILE) - .infinity_string(options::GUILE) - .build_unchecked(); -const_assert!(GUILE_STRING.is_valid()); + .nan_string(options::GUILE) + .inf_string(options::GUILE) + .infinity_string(options::GUILE) + .build_strict(); /// Number format for a `Clojure` literal floating-point number. #[rustfmt::skip] pub const CLOJURE_LITERAL: Options = Options::builder() - .nan_string(options::CLOJURE_LITERAL) - .inf_string(options::CLOJURE_LITERAL) - .infinity_string(options::CLOJURE_LITERAL) - .build_unchecked(); -const_assert!(CLOJURE_LITERAL.is_valid()); + .nan_string(options::CLOJURE_LITERAL) + .inf_string(options::CLOJURE_LITERAL) + .infinity_string(options::CLOJURE_LITERAL) + .build_strict(); /// Number format to parse a `Clojure` float from string. #[rustfmt::skip] pub const CLOJURE_STRING: Options = Options::builder() - .inf_string(options::CLOJURE_STRING_INF) - .infinity_string(options::CLOJURE_STRING_INFINITY) - .build_unchecked(); -const_assert!(CLOJURE_STRING.is_valid()); + .inf_string(options::CLOJURE_STRING_INF) + .infinity_string(options::CLOJURE_STRING_INFINITY) + .build_strict(); /// Number format for an `Erlang` literal floating-point number. #[rustfmt::skip] pub const ERLANG_LITERAL: Options = Options::builder() - .nan_string(options::ERLANG_LITERAL_NAN) - .build_unchecked(); -const_assert!(ERLANG_LITERAL.is_valid()); + .nan_string(options::ERLANG_LITERAL_NAN) + .build_strict(); /// Number format to parse an `Erlang` float from string. #[rustfmt::skip] pub const ERLANG_STRING: Options = Options::builder() - .nan_string(options::ERLANG_STRING) - .inf_string(options::ERLANG_STRING) - .infinity_string(options::ERLANG_STRING) - .build_unchecked(); -const_assert!(ERLANG_STRING.is_valid()); + .nan_string(options::ERLANG_STRING) + .inf_string(options::ERLANG_STRING) + .infinity_string(options::ERLANG_STRING) + .build_strict(); /// Number format for an `Elm` literal floating-point number. #[rustfmt::skip] pub const ELM_LITERAL: Options = Options::builder() - .nan_string(options::ELM_LITERAL) - .inf_string(options::ELM_LITERAL) - .infinity_string(options::ELM_LITERAL) - .build_unchecked(); -const_assert!(ELM_LITERAL.is_valid()); + .nan_string(options::ELM_LITERAL) + .inf_string(options::ELM_LITERAL) + .infinity_string(options::ELM_LITERAL) + .build_strict(); /// Number format to parse an `Elm` float from string. #[rustfmt::skip] pub const ELM_STRING: Options = Options::builder() - .nan_string(options::ELM_STRING_NAN) - .inf_string(options::ELM_STRING_INF) - .infinity_string(options::ELM_STRING_INFINITY) - .build_unchecked(); -const_assert!(ELM_STRING.is_valid()); + .nan_string(options::ELM_STRING_NAN) + .inf_string(options::ELM_STRING_INF) + .infinity_string(options::ELM_STRING_INFINITY) + .build_strict(); /// Number format for a `Scala` literal floating-point number. #[rustfmt::skip] pub const SCALA_LITERAL: Options = Options::builder() - .nan_string(options::SCALA_LITERAL) - .inf_string(options::SCALA_LITERAL) - .infinity_string(options::SCALA_LITERAL) - .build_unchecked(); -const_assert!(SCALA_LITERAL.is_valid()); + .nan_string(options::SCALA_LITERAL) + .inf_string(options::SCALA_LITERAL) + .infinity_string(options::SCALA_LITERAL) + .build_strict(); /// Number format to parse a `Scala` float from string. #[rustfmt::skip] pub const SCALA_STRING: Options = Options::builder() - .inf_string(options::SCALA_STRING_INF) - .infinity_string(options::SCALA_STRING_INFINITY) - .build_unchecked(); -const_assert!(SCALA_STRING.is_valid()); + .inf_string(options::SCALA_STRING_INF) + .infinity_string(options::SCALA_STRING_INFINITY) + .build_strict(); /// Number format for an `Elixir` literal floating-point number. #[rustfmt::skip] pub const ELIXIR_LITERAL: Options = Options::builder() - .nan_string(options::ELIXIR) - .inf_string(options::ELIXIR) - .infinity_string(options::ELIXIR) - .build_unchecked(); -const_assert!(ELIXIR_LITERAL.is_valid()); + .nan_string(options::ELIXIR) + .inf_string(options::ELIXIR) + .infinity_string(options::ELIXIR) + .build_strict(); /// Number format to parse an `Elixir` float from string. #[rustfmt::skip] pub const ELIXIR_STRING: Options = Options::builder() - .nan_string(options::ELIXIR) - .inf_string(options::ELIXIR) - .infinity_string(options::ELIXIR) - .build_unchecked(); -const_assert!(ELIXIR_STRING.is_valid()); + .nan_string(options::ELIXIR) + .inf_string(options::ELIXIR) + .infinity_string(options::ELIXIR) + .build_strict(); /// Number format for a `FORTRAN` literal floating-point number. #[rustfmt::skip] pub const FORTRAN_LITERAL: Options = Options::builder() - .nan_string(options::FORTRAN_LITERAL) - .inf_string(options::FORTRAN_LITERAL) - .infinity_string(options::FORTRAN_LITERAL) - .build_unchecked(); -const_assert!(FORTRAN_LITERAL.is_valid()); + .nan_string(options::FORTRAN_LITERAL) + .inf_string(options::FORTRAN_LITERAL) + .infinity_string(options::FORTRAN_LITERAL) + .build_strict(); /// Number format for a `D` literal floating-point number. #[rustfmt::skip] pub const D_LITERAL: Options = Options::builder() - .nan_string(options::D_LITERAL) - .inf_string(options::D_LITERAL) - .infinity_string(options::D_LITERAL) - .build_unchecked(); -const_assert!(D_LITERAL.is_valid()); + .nan_string(options::D_LITERAL) + .inf_string(options::D_LITERAL) + .infinity_string(options::D_LITERAL) + .build_strict(); /// Number format for a `Coffeescript` literal floating-point number. #[rustfmt::skip] pub const COFFEESCRIPT_LITERAL: Options = Options::builder() - .inf_string(options::COFFEESCRIPT_INF) - .infinity_string(options::COFFEESCRIPT_INFINITY) - .build_unchecked(); -const_assert!(COFFEESCRIPT_LITERAL.is_valid()); + .inf_string(options::COFFEESCRIPT_INF) + .infinity_string(options::COFFEESCRIPT_INFINITY) + .build_strict(); /// Number format to parse a `Coffeescript` float from string. #[rustfmt::skip] pub const COFFEESCRIPT_STRING: Options = Options::builder() - .inf_string(options::COFFEESCRIPT_INF) - .infinity_string(options::COFFEESCRIPT_INFINITY) - .build_unchecked(); -const_assert!(COFFEESCRIPT_STRING.is_valid()); + .inf_string(options::COFFEESCRIPT_INF) + .infinity_string(options::COFFEESCRIPT_INFINITY) + .build_strict(); /// Number format for a `COBOL` literal floating-point number. #[rustfmt::skip] pub const COBOL_LITERAL: Options = Options::builder() - .nan_string(options::COBOL) - .inf_string(options::COBOL) - .infinity_string(options::COBOL) - .build_unchecked(); -const_assert!(COBOL_LITERAL.is_valid()); + .nan_string(options::COBOL) + .inf_string(options::COBOL) + .infinity_string(options::COBOL) + .build_strict(); /// Number format to parse a `COBOL` float from string. #[rustfmt::skip] pub const COBOL_STRING: Options = Options::builder() - .nan_string(options::COBOL) - .inf_string(options::COBOL) - .infinity_string(options::COBOL) - .build_unchecked(); -const_assert!(COBOL_STRING.is_valid()); + .nan_string(options::COBOL) + .inf_string(options::COBOL) + .infinity_string(options::COBOL) + .build_strict(); /// Number format for an `F#` literal floating-point number. #[rustfmt::skip] pub const FSHARP_LITERAL: Options = Options::builder() - .nan_string(options::FSHARP_LITERAL_NAN) - .inf_string(options::FSHARP_LITERAL_INF) - .infinity_string(options::FSHARP_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(FSHARP_LITERAL.is_valid()); + .nan_string(options::FSHARP_LITERAL_NAN) + .inf_string(options::FSHARP_LITERAL_INF) + .infinity_string(options::FSHARP_LITERAL_INFINITY) + .build_strict(); /// Number format for a Visual Basic literal floating-point number. #[rustfmt::skip] pub const VB_LITERAL: Options = Options::builder() - .nan_string(options::VB_LITERAL) - .inf_string(options::VB_LITERAL) - .infinity_string(options::VB_LITERAL) - .build_unchecked(); -const_assert!(VB_LITERAL.is_valid()); + .nan_string(options::VB_LITERAL) + .inf_string(options::VB_LITERAL) + .infinity_string(options::VB_LITERAL) + .build_strict(); /// Number format to parse a `Visual Basic` float from string. #[rustfmt::skip] pub const VB_STRING: Options = Options::builder() - .inf_string(options::VB_STRING_INF) - .infinity_string(options::VB_STRING_INFINITY) - .build_unchecked(); -const_assert!(VB_STRING.is_valid()); + .inf_string(options::VB_STRING_INF) + .infinity_string(options::VB_STRING_INFINITY) + .build_strict(); /// Number format for an `OCaml` literal floating-point number. #[rustfmt::skip] pub const OCAML_LITERAL: Options = Options::builder() - .nan_string(options::OCAML_LITERAL_NAN) - .inf_string(options::OCAML_LITERAL_INF) - .infinity_string(options::OCAML_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(OCAML_LITERAL.is_valid()); + .nan_string(options::OCAML_LITERAL_NAN) + .inf_string(options::OCAML_LITERAL_INF) + .infinity_string(options::OCAML_LITERAL_INFINITY) + .build_strict(); /// Number format for an `Objective-C` literal floating-point number. #[rustfmt::skip] pub const OBJECTIVEC_LITERAL: Options = Options::builder() - .nan_string(options::OBJECTIVEC) - .inf_string(options::OBJECTIVEC) - .infinity_string(options::OBJECTIVEC) - .build_unchecked(); -const_assert!(OBJECTIVEC_LITERAL.is_valid()); + .nan_string(options::OBJECTIVEC) + .inf_string(options::OBJECTIVEC) + .infinity_string(options::OBJECTIVEC) + .build_strict(); /// Number format to parse an `Objective-C` float from string. #[rustfmt::skip] pub const OBJECTIVEC_STRING: Options = Options::builder() - .nan_string(options::OBJECTIVEC) - .inf_string(options::OBJECTIVEC) - .infinity_string(options::OBJECTIVEC) - .build_unchecked(); -const_assert!(OBJECTIVEC_STRING.is_valid()); + .nan_string(options::OBJECTIVEC) + .inf_string(options::OBJECTIVEC) + .infinity_string(options::OBJECTIVEC) + .build_strict(); /// Number format for an `ReasonML` literal floating-point number. #[rustfmt::skip] pub const REASONML_LITERAL: Options = Options::builder() - .nan_string(options::REASONML_LITERAL_NAN) - .inf_string(options::REASONML_LITERAL_INF) - .infinity_string(options::REASONML_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(REASONML_LITERAL.is_valid()); + .nan_string(options::REASONML_LITERAL_NAN) + .inf_string(options::REASONML_LITERAL_INF) + .infinity_string(options::REASONML_LITERAL_INFINITY) + .build_strict(); /// Number format for a `MATLAB` literal floating-point number. #[rustfmt::skip] pub const MATLAB_LITERAL: Options = Options::builder() - .inf_string(options::MATLAB_LITERAL_INF) - .infinity_string(options::MATLAB_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(MATLAB_LITERAL.is_valid()); + .inf_string(options::MATLAB_LITERAL_INF) + .infinity_string(options::MATLAB_LITERAL_INFINITY) + .build_strict(); /// Number format for a `Zig` literal floating-point number. #[rustfmt::skip] pub const ZIG_LITERAL: Options = Options::builder() - .nan_string(options::ZIG_LITERAL) - .inf_string(options::ZIG_LITERAL) - .infinity_string(options::ZIG_LITERAL) - .build_unchecked(); -const_assert!(ZIG_LITERAL.is_valid()); + .nan_string(options::ZIG_LITERAL) + .inf_string(options::ZIG_LITERAL) + .infinity_string(options::ZIG_LITERAL) + .build_strict(); /// Number format for a `Sage` literal floating-point number. #[rustfmt::skip] pub const SAGE_LITERAL: Options = Options::builder() - .inf_string(options::SAGE_LITERAL_INF) - .infinity_string(options::SAGE_LITERAL_INFINITY) - .build_unchecked(); -const_assert!(SAGE_LITERAL.is_valid()); + .inf_string(options::SAGE_LITERAL_INF) + .infinity_string(options::SAGE_LITERAL_INFINITY) + .build_strict(); /// Number format for a `JSON` literal floating-point number. #[rustfmt::skip] pub const JSON: Options = Options::builder() - .nan_string(options::JSON) - .inf_string(options::JSON) - .infinity_string(options::JSON) - .build_unchecked(); -const_assert!(JSON.is_valid()); + .nan_string(options::JSON) + .inf_string(options::JSON) + .infinity_string(options::JSON) + .build_strict(); /// Number format for a `TOML` literal floating-point number. #[rustfmt::skip] pub const TOML: Options = Options::builder() - .nan_string(options::TOML) - .inf_string(options::TOML) - .infinity_string(options::TOML) - .build_unchecked(); -const_assert!(TOML.is_valid()); + .nan_string(options::TOML) + .inf_string(options::TOML) + .infinity_string(options::TOML) + .build_strict(); /// Number format for a `YAML` literal floating-point number. #[rustfmt::skip] @@ -1024,42 +1470,37 @@ pub const YAML: Options = JSON; /// Number format for an `XML` literal floating-point number. #[rustfmt::skip] pub const XML: Options = Options::builder() - .inf_string(options::XML_INF) - .infinity_string(options::XML_INFINITY) - .build_unchecked(); -const_assert!(XML.is_valid()); + .inf_string(options::XML_INF) + .infinity_string(options::XML_INFINITY) + .build_strict(); /// Number format for a `SQLite` literal floating-point number. #[rustfmt::skip] pub const SQLITE: Options = Options::builder() - .nan_string(options::SQLITE) - .inf_string(options::SQLITE) - .infinity_string(options::SQLITE) - .build_unchecked(); -const_assert!(SQLITE.is_valid()); + .nan_string(options::SQLITE) + .inf_string(options::SQLITE) + .infinity_string(options::SQLITE) + .build_strict(); /// Number format for a `PostgreSQL` literal floating-point number. #[rustfmt::skip] pub const POSTGRESQL: Options = Options::builder() - .nan_string(options::POSTGRESQL) - .inf_string(options::POSTGRESQL) - .infinity_string(options::POSTGRESQL) - .build_unchecked(); -const_assert!(POSTGRESQL.is_valid()); + .nan_string(options::POSTGRESQL) + .inf_string(options::POSTGRESQL) + .infinity_string(options::POSTGRESQL) + .build_strict(); /// Number format for a `MySQL` literal floating-point number. #[rustfmt::skip] pub const MYSQL: Options = Options::builder() - .nan_string(options::MYSQL) - .inf_string(options::MYSQL) - .infinity_string(options::MYSQL) - .build_unchecked(); -const_assert!(MYSQL.is_valid()); + .nan_string(options::MYSQL) + .inf_string(options::MYSQL) + .infinity_string(options::MYSQL) + .build_strict(); /// Number format for a `MongoDB` literal floating-point number. #[rustfmt::skip] pub const MONGODB: Options = Options::builder() - .inf_string(options::MONGODB_INF) - .infinity_string(options::MONGODB_INFINITY) - .build_unchecked(); -const_assert!(MONGODB.is_valid()); + .inf_string(options::MONGODB_INF) + .infinity_string(options::MONGODB_INFINITY) + .build_strict(); diff --git a/lexical-parse-float/src/table_decimal.rs b/lexical-parse-float/src/table_decimal.rs index 39505f1f..0ede3a2a 100644 --- a/lexical-parse-float/src/table_decimal.rs +++ b/lexical-parse-float/src/table_decimal.rs @@ -3,8 +3,6 @@ #![doc(hidden)] #![cfg(not(feature = "compact"))] -use static_assertions::const_assert; - #[cfg(not(feature = "radix"))] use crate::bigint::Limb; use crate::limits::{f32_exponent_limit, f64_exponent_limit, f64_mantissa_limit, u64_power_limit}; @@ -113,8 +111,8 @@ pub const SMALL_INT_POW5: [u64; 28] = [ 1490116119384765625, 7450580596923828125, ]; -const_assert!(SMALL_INT_POW5.len() > f64_mantissa_limit(5) as usize); -const_assert!(SMALL_INT_POW5.len() == u64_power_limit(5) as usize + 1); +const _: () = assert!(SMALL_INT_POW5.len() > f64_mantissa_limit(5) as usize); +const _: () = assert!(SMALL_INT_POW5.len() == u64_power_limit(5) as usize + 1); /// Pre-computed, small powers-of-10. pub const SMALL_INT_POW10: [u64; 20] = [ @@ -139,20 +137,20 @@ pub const SMALL_INT_POW10: [u64; 20] = [ 1000000000000000000, 10000000000000000000, ]; -const_assert!(SMALL_INT_POW10.len() > f64_mantissa_limit(10) as usize); -const_assert!(SMALL_INT_POW10.len() == u64_power_limit(10) as usize + 1); +const _: () = assert!(SMALL_INT_POW10.len() > f64_mantissa_limit(10) as usize); +const _: () = assert!(SMALL_INT_POW10.len() == u64_power_limit(10) as usize + 1); /// Pre-computed, small powers-of-10. pub const SMALL_F32_POW10: [f32; 16] = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; -const_assert!(SMALL_F32_POW10.len() > f32_exponent_limit(10).1 as usize); +const _: () = assert!(SMALL_F32_POW10.len() > f32_exponent_limit(10).1 as usize); /// Pre-computed, small powers-of-10. pub const SMALL_F64_POW10: [f64; 32] = [ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., ]; -const_assert!(SMALL_F64_POW10.len() > f64_exponent_limit(10).1 as usize); +const _: () = assert!(SMALL_F64_POW10.len() > f64_exponent_limit(10).1 as usize); /// Pre-computed large power-of-5 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] diff --git a/lexical-parse-float/src/table_radix.rs b/lexical-parse-float/src/table_radix.rs index 58cbac81..74d2f248 100644 --- a/lexical-parse-float/src/table_radix.rs +++ b/lexical-parse-float/src/table_radix.rs @@ -6,7 +6,6 @@ #![allow(clippy::excessive_precision)] // reason = "auto-generated values that need to be exact" use lexical_util::assert::debug_assert_radix; -use static_assertions::const_assert; use crate::bigint::Limb; use crate::limits::{f32_exponent_limit, f64_exponent_limit, f64_mantissa_limit, u64_power_limit}; @@ -738,15 +737,15 @@ pub const SMALL_INT_POW3: [u64; 41] = [ 4052555153018976267, 12157665459056928801, ]; -const_assert!(SMALL_INT_POW3.len() > f64_mantissa_limit(3) as usize); -const_assert!(SMALL_INT_POW3.len() == u64_power_limit(3) as usize + 1); +const _: () = assert!(SMALL_INT_POW3.len() > f64_mantissa_limit(3) as usize); +const _: () = assert!(SMALL_INT_POW3.len() == u64_power_limit(3) as usize + 1); /// Pre-computed, small powers-of-3. pub const SMALL_F32_POW3: [f32; 16] = [ 1.0, 3.0, 9.0, 27.0, 81.0, 243.0, 729.0, 2187.0, 6561.0, 19683.0, 59049.0, 177147.0, 531441.0, 1594323.0, 4782969.0, 14348907.0, ]; -const_assert!(SMALL_F32_POW3.len() > f32_exponent_limit(3).1 as usize); +const _: () = assert!(SMALL_F32_POW3.len() > f32_exponent_limit(3).1 as usize); /// Pre-computed, small powers-of-3. pub const SMALL_F64_POW3: [f64; 34] = [ @@ -785,7 +784,7 @@ pub const SMALL_F64_POW3: [f64; 34] = [ 1853020188851841.0, 5559060566555523.0, ]; -const_assert!(SMALL_F64_POW3.len() > f64_exponent_limit(3).1 as usize); +const _: () = assert!(SMALL_F64_POW3.len() > f64_exponent_limit(3).1 as usize); /// Pre-computed large power-of-3 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -810,7 +809,7 @@ pub const LARGE_POW3_STEP: u32 = 200; /// Pre-computed, small powers-of-5. pub const SMALL_F32_POW5: [f32; 11] = [1.0, 5.0, 25.0, 125.0, 625.0, 3125.0, 15625.0, 78125.0, 390625.0, 1953125.0, 9765625.0]; -const_assert!(SMALL_F32_POW5.len() > f32_exponent_limit(5).1 as usize); +const _: () = assert!(SMALL_F32_POW5.len() > f32_exponent_limit(5).1 as usize); /// Pre-computed, small powers-of-5. pub const SMALL_F64_POW5: [f64; 23] = [ @@ -838,7 +837,7 @@ pub const SMALL_F64_POW5: [f64; 23] = [ 476837158203125.0, 2384185791015625.0, ]; -const_assert!(SMALL_F64_POW5.len() > f64_exponent_limit(5).1 as usize); +const _: () = assert!(SMALL_F64_POW5.len() > f64_exponent_limit(5).1 as usize); /// Pre-computed, small powers-of-6. pub const SMALL_INT_POW6: [u64; 25] = [ @@ -868,8 +867,8 @@ pub const SMALL_INT_POW6: [u64; 25] = [ 789730223053602816, 4738381338321616896, ]; -const_assert!(SMALL_INT_POW6.len() > f64_mantissa_limit(6) as usize); -const_assert!(SMALL_INT_POW6.len() == u64_power_limit(6) as usize + 1); +const _: () = assert!(SMALL_INT_POW6.len() > f64_mantissa_limit(6) as usize); +const _: () = assert!(SMALL_INT_POW6.len() == u64_power_limit(6) as usize + 1); /// Pre-computed, small powers-of-6. pub const SMALL_F32_POW6: [f32; 16] = [ @@ -890,7 +889,7 @@ pub const SMALL_F32_POW6: [f32; 16] = [ 78364164096.0, 470184984576.0, ]; -const_assert!(SMALL_F32_POW6.len() > f32_exponent_limit(6).1 as usize); +const _: () = assert!(SMALL_F32_POW6.len() > f32_exponent_limit(6).1 as usize); /// Pre-computed, small powers-of-6. pub const SMALL_F64_POW6: [f64; 34] = [ @@ -929,7 +928,7 @@ pub const SMALL_F64_POW6: [f64; 34] = [ 7.958661109946401e+24, 4.7751966659678405e+25, ]; -const_assert!(SMALL_F64_POW6.len() > f64_exponent_limit(6).1 as usize); +const _: () = assert!(SMALL_F64_POW6.len() > f64_exponent_limit(6).1 as usize); /// Pre-computed, small powers-of-7. pub const SMALL_INT_POW7: [u64; 23] = [ @@ -957,13 +956,13 @@ pub const SMALL_INT_POW7: [u64; 23] = [ 558545864083284007, 3909821048582988049, ]; -const_assert!(SMALL_INT_POW7.len() > f64_mantissa_limit(7) as usize); -const_assert!(SMALL_INT_POW7.len() == u64_power_limit(7) as usize + 1); +const _: () = assert!(SMALL_INT_POW7.len() > f64_mantissa_limit(7) as usize); +const _: () = assert!(SMALL_INT_POW7.len() == u64_power_limit(7) as usize + 1); /// Pre-computed, small powers-of-7. pub const SMALL_F32_POW7: [f32; 9] = [1.0, 7.0, 49.0, 343.0, 2401.0, 16807.0, 117649.0, 823543.0, 5764801.0]; -const_assert!(SMALL_F32_POW7.len() > f32_exponent_limit(7).1 as usize); +const _: () = assert!(SMALL_F32_POW7.len() > f32_exponent_limit(7).1 as usize); /// Pre-computed, small powers-of-7. pub const SMALL_F64_POW7: [f64; 19] = [ @@ -987,7 +986,7 @@ pub const SMALL_F64_POW7: [f64; 19] = [ 232630513987207.0, 1628413597910449.0, ]; -const_assert!(SMALL_F64_POW7.len() > f64_exponent_limit(7).1 as usize); +const _: () = assert!(SMALL_F64_POW7.len() > f64_exponent_limit(7).1 as usize); /// Pre-computed large power-of-7 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1033,12 +1032,12 @@ pub const SMALL_INT_POW9: [u64; 21] = [ 1350851717672992089, 12157665459056928801, ]; -const_assert!(SMALL_INT_POW9.len() > f64_mantissa_limit(9) as usize); -const_assert!(SMALL_INT_POW9.len() == u64_power_limit(9) as usize + 1); +const _: () = assert!(SMALL_INT_POW9.len() > f64_mantissa_limit(9) as usize); +const _: () = assert!(SMALL_INT_POW9.len() == u64_power_limit(9) as usize + 1); /// Pre-computed, small powers-of-9. pub const SMALL_F32_POW9: [f32; 8] = [1.0, 9.0, 81.0, 729.0, 6561.0, 59049.0, 531441.0, 4782969.0]; -const_assert!(SMALL_F32_POW9.len() > f32_exponent_limit(9).1 as usize); +const _: () = assert!(SMALL_F32_POW9.len() > f32_exponent_limit(9).1 as usize); /// Pre-computed, small powers-of-9. pub const SMALL_F64_POW9: [f64; 17] = [ @@ -1060,7 +1059,7 @@ pub const SMALL_F64_POW9: [f64; 17] = [ 205891132094649.0, 1853020188851841.0, ]; -const_assert!(SMALL_F64_POW9.len() > f64_exponent_limit(9).1 as usize); +const _: () = assert!(SMALL_F64_POW9.len() > f64_exponent_limit(9).1 as usize); /// Pre-computed large power-of-9 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1104,12 +1103,12 @@ pub const SMALL_INT_POW11: [u64; 19] = [ 505447028499293771, 5559917313492231481, ]; -const_assert!(SMALL_INT_POW11.len() > f64_mantissa_limit(11) as usize); -const_assert!(SMALL_INT_POW11.len() == u64_power_limit(11) as usize + 1); +const _: () = assert!(SMALL_INT_POW11.len() > f64_mantissa_limit(11) as usize); +const _: () = assert!(SMALL_INT_POW11.len() == u64_power_limit(11) as usize + 1); /// Pre-computed, small powers-of-11. pub const SMALL_F32_POW11: [f32; 7] = [1.0, 11.0, 121.0, 1331.0, 14641.0, 161051.0, 1771561.0]; -const_assert!(SMALL_F32_POW11.len() > f32_exponent_limit(11).1 as usize); +const _: () = assert!(SMALL_F32_POW11.len() > f32_exponent_limit(11).1 as usize); /// Pre-computed, small powers-of-11. pub const SMALL_F64_POW11: [f64; 16] = [ @@ -1130,7 +1129,7 @@ pub const SMALL_F64_POW11: [f64; 16] = [ 379749833583241.0, 4177248169415651.0, ]; -const_assert!(SMALL_F64_POW11.len() > f64_exponent_limit(11).1 as usize); +const _: () = assert!(SMALL_F64_POW11.len() > f64_exponent_limit(11).1 as usize); /// Pre-computed large power-of-11 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1173,8 +1172,8 @@ pub const SMALL_INT_POW12: [u64; 18] = [ 184884258895036416, 2218611106740436992, ]; -const_assert!(SMALL_INT_POW12.len() > f64_mantissa_limit(12) as usize); -const_assert!(SMALL_INT_POW12.len() == u64_power_limit(12) as usize + 1); +const _: () = assert!(SMALL_INT_POW12.len() > f64_mantissa_limit(12) as usize); +const _: () = assert!(SMALL_INT_POW12.len() == u64_power_limit(12) as usize + 1); /// Pre-computed, small powers-of-12. pub const SMALL_F32_POW12: [f32; 16] = [ @@ -1195,7 +1194,7 @@ pub const SMALL_F32_POW12: [f32; 16] = [ 1283918464548864.0, 1.5407021574586368e+16, ]; -const_assert!(SMALL_F32_POW12.len() > f32_exponent_limit(12).1 as usize); +const _: () = assert!(SMALL_F32_POW12.len() > f32_exponent_limit(12).1 as usize); /// Pre-computed, small powers-of-12. pub const SMALL_F64_POW12: [f64; 34] = [ @@ -1234,7 +1233,7 @@ pub const SMALL_F64_POW12: [f64; 34] = [ 3.418218918716685e+34, 4.101862702460022e+35, ]; -const_assert!(SMALL_F64_POW12.len() > f64_exponent_limit(12).1 as usize); +const _: () = assert!(SMALL_F64_POW12.len() > f64_exponent_limit(12).1 as usize); /// Pre-computed, small powers-of-13. pub const SMALL_INT_POW13: [u64; 18] = [ @@ -1257,12 +1256,12 @@ pub const SMALL_INT_POW13: [u64; 18] = [ 665416609183179841, 8650415919381337933, ]; -const_assert!(SMALL_INT_POW13.len() > f64_mantissa_limit(13) as usize); -const_assert!(SMALL_INT_POW13.len() == u64_power_limit(13) as usize + 1); +const _: () = assert!(SMALL_INT_POW13.len() > f64_mantissa_limit(13) as usize); +const _: () = assert!(SMALL_INT_POW13.len() == u64_power_limit(13) as usize + 1); /// Pre-computed, small powers-of-13. pub const SMALL_F32_POW13: [f32; 7] = [1.0, 13.0, 169.0, 2197.0, 28561.0, 371293.0, 4826809.0]; -const_assert!(SMALL_F32_POW13.len() > f32_exponent_limit(13).1 as usize); +const _: () = assert!(SMALL_F32_POW13.len() > f32_exponent_limit(13).1 as usize); /// Pre-computed, small powers-of-13. pub const SMALL_F64_POW13: [f64; 15] = [ @@ -1282,7 +1281,7 @@ pub const SMALL_F64_POW13: [f64; 15] = [ 302875106592253.0, 3937376385699289.0, ]; -const_assert!(SMALL_F64_POW13.len() > f64_exponent_limit(13).1 as usize); +const _: () = assert!(SMALL_F64_POW13.len() > f64_exponent_limit(13).1 as usize); /// Pre-computed large power-of-13 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1324,13 +1323,13 @@ pub const SMALL_INT_POW14: [u64; 17] = [ 155568095557812224, 2177953337809371136, ]; -const_assert!(SMALL_INT_POW14.len() > f64_mantissa_limit(14) as usize); -const_assert!(SMALL_INT_POW14.len() == u64_power_limit(14) as usize + 1); +const _: () = assert!(SMALL_INT_POW14.len() > f64_mantissa_limit(14) as usize); +const _: () = assert!(SMALL_INT_POW14.len() == u64_power_limit(14) as usize + 1); /// Pre-computed, small powers-of-14. pub const SMALL_F32_POW14: [f32; 9] = [1.0, 14.0, 196.0, 2744.0, 38416.0, 537824.0, 7529536.0, 105413504.0, 1475789056.0]; -const_assert!(SMALL_F32_POW14.len() > f32_exponent_limit(14).1 as usize); +const _: () = assert!(SMALL_F32_POW14.len() > f32_exponent_limit(14).1 as usize); /// Pre-computed, small powers-of-14. pub const SMALL_F64_POW14: [f64; 19] = [ @@ -1354,7 +1353,7 @@ pub const SMALL_F64_POW14: [f64; 19] = [ 3.0491346729331196e+19, 4.2687885421063674e+20, ]; -const_assert!(SMALL_F64_POW14.len() > f64_exponent_limit(14).1 as usize); +const _: () = assert!(SMALL_F64_POW14.len() > f64_exponent_limit(14).1 as usize); /// Pre-computed, small powers-of-15. pub const SMALL_INT_POW15: [u64; 17] = [ @@ -1376,12 +1375,12 @@ pub const SMALL_INT_POW15: [u64; 17] = [ 437893890380859375, 6568408355712890625, ]; -const_assert!(SMALL_INT_POW15.len() > f64_mantissa_limit(15) as usize); -const_assert!(SMALL_INT_POW15.len() == u64_power_limit(15) as usize + 1); +const _: () = assert!(SMALL_INT_POW15.len() > f64_mantissa_limit(15) as usize); +const _: () = assert!(SMALL_INT_POW15.len() == u64_power_limit(15) as usize + 1); /// Pre-computed, small powers-of-15. pub const SMALL_F32_POW15: [f32; 7] = [1.0, 15.0, 225.0, 3375.0, 50625.0, 759375.0, 11390625.0]; -const_assert!(SMALL_F32_POW15.len() > f32_exponent_limit(15).1 as usize); +const _: () = assert!(SMALL_F32_POW15.len() > f32_exponent_limit(15).1 as usize); /// Pre-computed, small powers-of-15. pub const SMALL_F64_POW15: [f64; 14] = [ @@ -1400,7 +1399,7 @@ pub const SMALL_F64_POW15: [f64; 14] = [ 129746337890625.0, 1946195068359375.0, ]; -const_assert!(SMALL_F64_POW15.len() > f64_exponent_limit(15).1 as usize); +const _: () = assert!(SMALL_F64_POW15.len() > f64_exponent_limit(15).1 as usize); /// Pre-computed large power-of-15 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1441,12 +1440,12 @@ pub const SMALL_INT_POW17: [u64; 16] = [ 168377826559400929, 2862423051509815793, ]; -const_assert!(SMALL_INT_POW17.len() > f64_mantissa_limit(17) as usize); -const_assert!(SMALL_INT_POW17.len() == u64_power_limit(17) as usize + 1); +const _: () = assert!(SMALL_INT_POW17.len() > f64_mantissa_limit(17) as usize); +const _: () = assert!(SMALL_INT_POW17.len() == u64_power_limit(17) as usize + 1); /// Pre-computed, small powers-of-17. pub const SMALL_F32_POW17: [f32; 6] = [1.0, 17.0, 289.0, 4913.0, 83521.0, 1419857.0]; -const_assert!(SMALL_F32_POW17.len() > f32_exponent_limit(17).1 as usize); +const _: () = assert!(SMALL_F32_POW17.len() > f32_exponent_limit(17).1 as usize); /// Pre-computed, small powers-of-17. pub const SMALL_F64_POW17: [f64; 13] = [ @@ -1464,7 +1463,7 @@ pub const SMALL_F64_POW17: [f64; 13] = [ 34271896307633.0, 582622237229761.0, ]; -const_assert!(SMALL_F64_POW17.len() > f64_exponent_limit(17).1 as usize); +const _: () = assert!(SMALL_F64_POW17.len() > f64_exponent_limit(17).1 as usize); /// Pre-computed large power-of-17 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1505,13 +1504,13 @@ pub const SMALL_INT_POW18: [u64; 16] = [ 374813367582081024, 6746640616477458432, ]; -const_assert!(SMALL_INT_POW18.len() > f64_mantissa_limit(18) as usize); -const_assert!(SMALL_INT_POW18.len() == u64_power_limit(18) as usize + 1); +const _: () = assert!(SMALL_INT_POW18.len() > f64_mantissa_limit(18) as usize); +const _: () = assert!(SMALL_INT_POW18.len() == u64_power_limit(18) as usize + 1); /// Pre-computed, small powers-of-18. pub const SMALL_F32_POW18: [f32; 8] = [1.0, 18.0, 324.0, 5832.0, 104976.0, 1889568.0, 34012224.0, 612220032.0]; -const_assert!(SMALL_F32_POW18.len() > f32_exponent_limit(18).1 as usize); +const _: () = assert!(SMALL_F32_POW18.len() > f32_exponent_limit(18).1 as usize); /// Pre-computed, small powers-of-18. pub const SMALL_F64_POW18: [f64; 17] = [ @@ -1533,7 +1532,7 @@ pub const SMALL_F64_POW18: [f64; 17] = [ 6.746640616477458e+18, 1.2143953109659425e+20, ]; -const_assert!(SMALL_F64_POW18.len() > f64_exponent_limit(18).1 as usize); +const _: () = assert!(SMALL_F64_POW18.len() > f64_exponent_limit(18).1 as usize); /// Pre-computed, small powers-of-19. pub const SMALL_INT_POW19: [u64; 16] = [ @@ -1554,12 +1553,12 @@ pub const SMALL_INT_POW19: [u64; 16] = [ 799006685782884121, 15181127029874798299, ]; -const_assert!(SMALL_INT_POW19.len() > f64_mantissa_limit(19) as usize); -const_assert!(SMALL_INT_POW19.len() == u64_power_limit(19) as usize + 1); +const _: () = assert!(SMALL_INT_POW19.len() > f64_mantissa_limit(19) as usize); +const _: () = assert!(SMALL_INT_POW19.len() == u64_power_limit(19) as usize + 1); /// Pre-computed, small powers-of-19. pub const SMALL_F32_POW19: [f32; 6] = [1.0, 19.0, 361.0, 6859.0, 130321.0, 2476099.0]; -const_assert!(SMALL_F32_POW19.len() > f32_exponent_limit(19).1 as usize); +const _: () = assert!(SMALL_F32_POW19.len() > f32_exponent_limit(19).1 as usize); /// Pre-computed, small powers-of-19. pub const SMALL_F64_POW19: [f64; 13] = [ @@ -1577,7 +1576,7 @@ pub const SMALL_F64_POW19: [f64; 13] = [ 116490258898219.0, 2213314919066161.0, ]; -const_assert!(SMALL_F64_POW19.len() > f64_exponent_limit(19).1 as usize); +const _: () = assert!(SMALL_F64_POW19.len() > f64_exponent_limit(19).1 as usize); /// Pre-computed large power-of-19 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1617,8 +1616,8 @@ pub const SMALL_INT_POW20: [u64; 15] = [ 81920000000000000, 1638400000000000000, ]; -const_assert!(SMALL_INT_POW20.len() > f64_mantissa_limit(20) as usize); -const_assert!(SMALL_INT_POW20.len() == u64_power_limit(20) as usize + 1); +const _: () = assert!(SMALL_INT_POW20.len() > f64_mantissa_limit(20) as usize); +const _: () = assert!(SMALL_INT_POW20.len() == u64_power_limit(20) as usize + 1); /// Pre-computed, small powers-of-20. pub const SMALL_F32_POW20: [f32; 11] = [ @@ -1634,7 +1633,7 @@ pub const SMALL_F32_POW20: [f32; 11] = [ 512000000000.0, 10240000000000.0, ]; -const_assert!(SMALL_F32_POW20.len() > f32_exponent_limit(20).1 as usize); +const _: () = assert!(SMALL_F32_POW20.len() > f32_exponent_limit(20).1 as usize); /// Pre-computed, small powers-of-20. pub const SMALL_F64_POW20: [f64; 23] = [ @@ -1662,7 +1661,7 @@ pub const SMALL_F64_POW20: [f64; 23] = [ 2.097152e+27, 4.194304e+28, ]; -const_assert!(SMALL_F64_POW20.len() > f64_exponent_limit(20).1 as usize); +const _: () = assert!(SMALL_F64_POW20.len() > f64_exponent_limit(20).1 as usize); /// Pre-computed, small powers-of-21. pub const SMALL_INT_POW21: [u64; 15] = [ @@ -1682,12 +1681,12 @@ pub const SMALL_INT_POW21: [u64; 15] = [ 154472377739119461, 3243919932521508681, ]; -const_assert!(SMALL_INT_POW21.len() > f64_mantissa_limit(21) as usize); -const_assert!(SMALL_INT_POW21.len() == u64_power_limit(21) as usize + 1); +const _: () = assert!(SMALL_INT_POW21.len() > f64_mantissa_limit(21) as usize); +const _: () = assert!(SMALL_INT_POW21.len() == u64_power_limit(21) as usize + 1); /// Pre-computed, small powers-of-21. pub const SMALL_F32_POW21: [f32; 6] = [1.0, 21.0, 441.0, 9261.0, 194481.0, 4084101.0]; -const_assert!(SMALL_F32_POW21.len() > f32_exponent_limit(21).1 as usize); +const _: () = assert!(SMALL_F32_POW21.len() > f32_exponent_limit(21).1 as usize); /// Pre-computed, small powers-of-21. pub const SMALL_F64_POW21: [f64; 13] = [ @@ -1705,7 +1704,7 @@ pub const SMALL_F64_POW21: [f64; 13] = [ 350277500542221.0, 7355827511386641.0, ]; -const_assert!(SMALL_F64_POW21.len() > f64_exponent_limit(21).1 as usize); +const _: () = assert!(SMALL_F64_POW21.len() > f64_exponent_limit(21).1 as usize); /// Pre-computed large power-of-21 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1745,12 +1744,12 @@ pub const SMALL_INT_POW22: [u64; 15] = [ 282810057883082752, 6221821273427820544, ]; -const_assert!(SMALL_INT_POW22.len() > f64_mantissa_limit(22) as usize); -const_assert!(SMALL_INT_POW22.len() == u64_power_limit(22) as usize + 1); +const _: () = assert!(SMALL_INT_POW22.len() > f64_mantissa_limit(22) as usize); +const _: () = assert!(SMALL_INT_POW22.len() == u64_power_limit(22) as usize + 1); /// Pre-computed, small powers-of-22. pub const SMALL_F32_POW22: [f32; 7] = [1.0, 22.0, 484.0, 10648.0, 234256.0, 5153632.0, 113379904.0]; -const_assert!(SMALL_F32_POW22.len() > f32_exponent_limit(22).1 as usize); +const _: () = assert!(SMALL_F32_POW22.len() > f32_exponent_limit(22).1 as usize); /// Pre-computed, small powers-of-22. pub const SMALL_F64_POW22: [f64; 16] = [ @@ -1771,7 +1770,7 @@ pub const SMALL_F64_POW22: [f64; 16] = [ 6.221821273427821e+18, 1.3688006801541205e+20, ]; -const_assert!(SMALL_F64_POW22.len() > f64_exponent_limit(22).1 as usize); +const _: () = assert!(SMALL_F64_POW22.len() > f64_exponent_limit(22).1 as usize); /// Pre-computed, small powers-of-23. pub const SMALL_INT_POW23: [u64; 15] = [ @@ -1791,12 +1790,12 @@ pub const SMALL_INT_POW23: [u64; 15] = [ 504036361936467383, 11592836324538749809, ]; -const_assert!(SMALL_INT_POW23.len() > f64_mantissa_limit(23) as usize); -const_assert!(SMALL_INT_POW23.len() == u64_power_limit(23) as usize + 1); +const _: () = assert!(SMALL_INT_POW23.len() > f64_mantissa_limit(23) as usize); +const _: () = assert!(SMALL_INT_POW23.len() == u64_power_limit(23) as usize + 1); /// Pre-computed, small powers-of-23. pub const SMALL_F32_POW23: [f32; 6] = [1.0, 23.0, 529.0, 12167.0, 279841.0, 6436343.0]; -const_assert!(SMALL_F32_POW23.len() > f32_exponent_limit(23).1 as usize); +const _: () = assert!(SMALL_F32_POW23.len() > f32_exponent_limit(23).1 as usize); /// Pre-computed, small powers-of-23. pub const SMALL_F64_POW23: [f64; 12] = [ @@ -1813,7 +1812,7 @@ pub const SMALL_F64_POW23: [f64; 12] = [ 41426511213649.0, 952809757913927.0, ]; -const_assert!(SMALL_F64_POW23.len() > f64_exponent_limit(23).1 as usize); +const _: () = assert!(SMALL_F64_POW23.len() > f64_exponent_limit(23).1 as usize); /// Pre-computed large power-of-23 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1852,8 +1851,8 @@ pub const SMALL_INT_POW24: [u64; 14] = [ 36520347436056576, 876488338465357824, ]; -const_assert!(SMALL_INT_POW24.len() > f64_mantissa_limit(24) as usize); -const_assert!(SMALL_INT_POW24.len() == u64_power_limit(24) as usize + 1); +const _: () = assert!(SMALL_INT_POW24.len() > f64_mantissa_limit(24) as usize); +const _: () = assert!(SMALL_INT_POW24.len() == u64_power_limit(24) as usize + 1); /// Pre-computed, small powers-of-24. pub const SMALL_F32_POW24: [f32; 16] = [ @@ -1874,7 +1873,7 @@ pub const SMALL_F32_POW24: [f32; 16] = [ 2.1035720123168588e+19, 5.048572829560461e+20, ]; -const_assert!(SMALL_F32_POW24.len() > f32_exponent_limit(24).1 as usize); +const _: () = assert!(SMALL_F32_POW24.len() > f32_exponent_limit(24).1 as usize); /// Pre-computed, small powers-of-24. pub const SMALL_F64_POW24: [f64; 34] = [ @@ -1913,7 +1912,7 @@ pub const SMALL_F64_POW24: [f64; 34] = [ 1.4681138466456645e+44, 3.523473231949595e+45, ]; -const_assert!(SMALL_F64_POW24.len() > f64_exponent_limit(24).1 as usize); +const _: () = assert!(SMALL_F64_POW24.len() > f64_exponent_limit(24).1 as usize); /// Pre-computed, small powers-of-25. pub const SMALL_INT_POW25: [u64; 14] = [ @@ -1932,12 +1931,12 @@ pub const SMALL_INT_POW25: [u64; 14] = [ 59604644775390625, 1490116119384765625, ]; -const_assert!(SMALL_INT_POW25.len() > f64_mantissa_limit(25) as usize); -const_assert!(SMALL_INT_POW25.len() == u64_power_limit(25) as usize + 1); +const _: () = assert!(SMALL_INT_POW25.len() > f64_mantissa_limit(25) as usize); +const _: () = assert!(SMALL_INT_POW25.len() == u64_power_limit(25) as usize + 1); /// Pre-computed, small powers-of-25. pub const SMALL_F32_POW25: [f32; 6] = [1.0, 25.0, 625.0, 15625.0, 390625.0, 9765625.0]; -const_assert!(SMALL_F32_POW25.len() > f32_exponent_limit(25).1 as usize); +const _: () = assert!(SMALL_F32_POW25.len() > f32_exponent_limit(25).1 as usize); /// Pre-computed, small powers-of-25. pub const SMALL_F64_POW25: [f64; 12] = [ @@ -1954,7 +1953,7 @@ pub const SMALL_F64_POW25: [f64; 12] = [ 95367431640625.0, 2384185791015625.0, ]; -const_assert!(SMALL_F64_POW25.len() > f64_exponent_limit(25).1 as usize); +const _: () = assert!(SMALL_F64_POW25.len() > f64_exponent_limit(25).1 as usize); /// Pre-computed large power-of-25 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -1993,13 +1992,13 @@ pub const SMALL_INT_POW26: [u64; 14] = [ 95428956661682176, 2481152873203736576, ]; -const_assert!(SMALL_INT_POW26.len() > f64_mantissa_limit(26) as usize); -const_assert!(SMALL_INT_POW26.len() == u64_power_limit(26) as usize + 1); +const _: () = assert!(SMALL_INT_POW26.len() > f64_mantissa_limit(26) as usize); +const _: () = assert!(SMALL_INT_POW26.len() == u64_power_limit(26) as usize + 1); /// Pre-computed, small powers-of-26. pub const SMALL_F32_POW26: [f32; 7] = [1.0, 26.0, 676.0, 17576.0, 456976.0, 11881376.0, 308915776.0]; -const_assert!(SMALL_F32_POW26.len() > f32_exponent_limit(26).1 as usize); +const _: () = assert!(SMALL_F32_POW26.len() > f32_exponent_limit(26).1 as usize); /// Pre-computed, small powers-of-26. pub const SMALL_F64_POW26: [f64; 15] = [ @@ -2019,7 +2018,7 @@ pub const SMALL_F64_POW26: [f64; 15] = [ 2.4811528732037366e+18, 6.450997470329715e+19, ]; -const_assert!(SMALL_F64_POW26.len() > f64_exponent_limit(26).1 as usize); +const _: () = assert!(SMALL_F64_POW26.len() > f64_exponent_limit(26).1 as usize); /// Pre-computed, small powers-of-27. pub const SMALL_INT_POW27: [u64; 14] = [ @@ -2038,12 +2037,12 @@ pub const SMALL_INT_POW27: [u64; 14] = [ 150094635296999121, 4052555153018976267, ]; -const_assert!(SMALL_INT_POW27.len() > f64_mantissa_limit(27) as usize); -const_assert!(SMALL_INT_POW27.len() == u64_power_limit(27) as usize + 1); +const _: () = assert!(SMALL_INT_POW27.len() > f64_mantissa_limit(27) as usize); +const _: () = assert!(SMALL_INT_POW27.len() == u64_power_limit(27) as usize + 1); /// Pre-computed, small powers-of-27. pub const SMALL_F32_POW27: [f32; 6] = [1.0, 27.0, 729.0, 19683.0, 531441.0, 14348907.0]; -const_assert!(SMALL_F32_POW27.len() > f32_exponent_limit(27).1 as usize); +const _: () = assert!(SMALL_F32_POW27.len() > f32_exponent_limit(27).1 as usize); /// Pre-computed, small powers-of-27. pub const SMALL_F64_POW27: [f64; 12] = [ @@ -2060,7 +2059,7 @@ pub const SMALL_F64_POW27: [f64; 12] = [ 205891132094649.0, 5559060566555523.0, ]; -const_assert!(SMALL_F64_POW27.len() > f64_exponent_limit(27).1 as usize); +const _: () = assert!(SMALL_F64_POW27.len() > f64_exponent_limit(27).1 as usize); /// Pre-computed large power-of-27 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -2099,13 +2098,13 @@ pub const SMALL_INT_POW28: [u64; 14] = [ 232218265089212416, 6502111422497947648, ]; -const_assert!(SMALL_INT_POW28.len() > f64_mantissa_limit(28) as usize); -const_assert!(SMALL_INT_POW28.len() == u64_power_limit(28) as usize + 1); +const _: () = assert!(SMALL_INT_POW28.len() > f64_mantissa_limit(28) as usize); +const _: () = assert!(SMALL_INT_POW28.len() == u64_power_limit(28) as usize + 1); /// Pre-computed, small powers-of-28. pub const SMALL_F32_POW28: [f32; 9] = [1.0, 28.0, 784.0, 21952.0, 614656.0, 17210368.0, 481890304.0, 13492928512.0, 377801998336.0]; -const_assert!(SMALL_F32_POW28.len() > f32_exponent_limit(28).1 as usize); +const _: () = assert!(SMALL_F32_POW28.len() > f32_exponent_limit(28).1 as usize); /// Pre-computed, small powers-of-28. pub const SMALL_F64_POW28: [f64; 19] = [ @@ -2129,7 +2128,7 @@ pub const SMALL_F64_POW28: [f64; 19] = [ 3.9965617985068985e+24, 1.1190373035819316e+26, ]; -const_assert!(SMALL_F64_POW28.len() > f64_exponent_limit(28).1 as usize); +const _: () = assert!(SMALL_F64_POW28.len() > f64_exponent_limit(28).1 as usize); /// Pre-computed, small powers-of-29. pub const SMALL_INT_POW29: [u64; 14] = [ @@ -2148,12 +2147,12 @@ pub const SMALL_INT_POW29: [u64; 14] = [ 353814783205469041, 10260628712958602189, ]; -const_assert!(SMALL_INT_POW29.len() > f64_mantissa_limit(29) as usize); -const_assert!(SMALL_INT_POW29.len() == u64_power_limit(29) as usize + 1); +const _: () = assert!(SMALL_INT_POW29.len() > f64_mantissa_limit(29) as usize); +const _: () = assert!(SMALL_INT_POW29.len() == u64_power_limit(29) as usize + 1); /// Pre-computed, small powers-of-29. pub const SMALL_F32_POW29: [f32; 5] = [1.0, 29.0, 841.0, 24389.0, 707281.0]; -const_assert!(SMALL_F32_POW29.len() > f32_exponent_limit(29).1 as usize); +const _: () = assert!(SMALL_F32_POW29.len() > f32_exponent_limit(29).1 as usize); /// Pre-computed, small powers-of-29. pub const SMALL_F64_POW29: [f64; 11] = [ @@ -2169,7 +2168,7 @@ pub const SMALL_F64_POW29: [f64; 11] = [ 14507145975869.0, 420707233300201.0, ]; -const_assert!(SMALL_F64_POW29.len() > f64_exponent_limit(29).1 as usize); +const _: () = assert!(SMALL_F64_POW29.len() > f64_exponent_limit(29).1 as usize); /// Pre-computed large power-of-29 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -2208,13 +2207,13 @@ pub const SMALL_INT_POW30: [u64; 14] = [ 531441000000000000, 15943230000000000000, ]; -const_assert!(SMALL_INT_POW30.len() > f64_mantissa_limit(30) as usize); -const_assert!(SMALL_INT_POW30.len() == u64_power_limit(30) as usize + 1); +const _: () = assert!(SMALL_INT_POW30.len() > f64_mantissa_limit(30) as usize); +const _: () = assert!(SMALL_INT_POW30.len() == u64_power_limit(30) as usize + 1); /// Pre-computed, small powers-of-30. pub const SMALL_F32_POW30: [f32; 7] = [1.0, 30.0, 900.0, 27000.0, 810000.0, 24300000.0, 729000000.0]; -const_assert!(SMALL_F32_POW30.len() > f32_exponent_limit(30).1 as usize); +const _: () = assert!(SMALL_F32_POW30.len() > f32_exponent_limit(30).1 as usize); /// Pre-computed, small powers-of-30. pub const SMALL_F64_POW30: [f64; 14] = [ @@ -2233,7 +2232,7 @@ pub const SMALL_F64_POW30: [f64; 14] = [ 5.31441e+17, 1.594323e+19, ]; -const_assert!(SMALL_F64_POW30.len() > f64_exponent_limit(30).1 as usize); +const _: () = assert!(SMALL_F64_POW30.len() > f64_exponent_limit(30).1 as usize); /// Pre-computed, small powers-of-31. pub const SMALL_INT_POW31: [u64; 13] = [ @@ -2251,12 +2250,12 @@ pub const SMALL_INT_POW31: [u64; 13] = [ 25408476896404831, 787662783788549761, ]; -const_assert!(SMALL_INT_POW31.len() > f64_mantissa_limit(31) as usize); -const_assert!(SMALL_INT_POW31.len() == u64_power_limit(31) as usize + 1); +const _: () = assert!(SMALL_INT_POW31.len() > f64_mantissa_limit(31) as usize); +const _: () = assert!(SMALL_INT_POW31.len() == u64_power_limit(31) as usize + 1); /// Pre-computed, small powers-of-31. pub const SMALL_F32_POW31: [f32; 5] = [1.0, 31.0, 961.0, 29791.0, 923521.0]; -const_assert!(SMALL_F32_POW31.len() > f32_exponent_limit(31).1 as usize); +const _: () = assert!(SMALL_F32_POW31.len() > f32_exponent_limit(31).1 as usize); /// Pre-computed, small powers-of-31. pub const SMALL_F64_POW31: [f64; 11] = [ @@ -2272,7 +2271,7 @@ pub const SMALL_F64_POW31: [f64; 11] = [ 26439622160671.0, 819628286980801.0, ]; -const_assert!(SMALL_F64_POW31.len() > f64_exponent_limit(31).1 as usize); +const _: () = assert!(SMALL_F64_POW31.len() > f64_exponent_limit(31).1 as usize); /// Pre-computed large power-of-31 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -2310,12 +2309,12 @@ pub const SMALL_INT_POW33: [u64; 13] = [ 50542106513726817, 1667889514952984961, ]; -const_assert!(SMALL_INT_POW33.len() > f64_mantissa_limit(33) as usize); -const_assert!(SMALL_INT_POW33.len() == u64_power_limit(33) as usize + 1); +const _: () = assert!(SMALL_INT_POW33.len() > f64_mantissa_limit(33) as usize); +const _: () = assert!(SMALL_INT_POW33.len() == u64_power_limit(33) as usize + 1); /// Pre-computed, small powers-of-33. pub const SMALL_F32_POW33: [f32; 5] = [1.0, 33.0, 1089.0, 35937.0, 1185921.0]; -const_assert!(SMALL_F32_POW33.len() > f32_exponent_limit(33).1 as usize); +const _: () = assert!(SMALL_F32_POW33.len() > f32_exponent_limit(33).1 as usize); /// Pre-computed, small powers-of-33. pub const SMALL_F64_POW33: [f64; 11] = [ @@ -2331,7 +2330,7 @@ pub const SMALL_F64_POW33: [f64; 11] = [ 46411484401953.0, 1531578985264449.0, ]; -const_assert!(SMALL_F64_POW33.len() > f64_exponent_limit(33).1 as usize); +const _: () = assert!(SMALL_F64_POW33.len() > f64_exponent_limit(33).1 as usize); /// Pre-computed large power-of-33 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -2369,12 +2368,12 @@ pub const SMALL_INT_POW34: [u64; 13] = [ 70188843638032384, 2386420683693101056, ]; -const_assert!(SMALL_INT_POW34.len() > f64_mantissa_limit(34) as usize); -const_assert!(SMALL_INT_POW34.len() == u64_power_limit(34) as usize + 1); +const _: () = assert!(SMALL_INT_POW34.len() > f64_mantissa_limit(34) as usize); +const _: () = assert!(SMALL_INT_POW34.len() == u64_power_limit(34) as usize + 1); /// Pre-computed, small powers-of-34. pub const SMALL_F32_POW34: [f32; 6] = [1.0, 34.0, 1156.0, 39304.0, 1336336.0, 45435424.0]; -const_assert!(SMALL_F32_POW34.len() > f32_exponent_limit(34).1 as usize); +const _: () = assert!(SMALL_F32_POW34.len() > f32_exponent_limit(34).1 as usize); /// Pre-computed, small powers-of-34. pub const SMALL_F64_POW34: [f64; 13] = [ @@ -2392,7 +2391,7 @@ pub const SMALL_F64_POW34: [f64; 13] = [ 7.018884363803238e+16, 2.386420683693101e+18, ]; -const_assert!(SMALL_F64_POW34.len() > f64_exponent_limit(34).1 as usize); +const _: () = assert!(SMALL_F64_POW34.len() > f64_exponent_limit(34).1 as usize); /// Pre-computed, small powers-of-35. pub const SMALL_INT_POW35: [u64; 13] = [ @@ -2410,12 +2409,12 @@ pub const SMALL_INT_POW35: [u64; 13] = [ 96549157373046875, 3379220508056640625, ]; -const_assert!(SMALL_INT_POW35.len() > f64_mantissa_limit(35) as usize); -const_assert!(SMALL_INT_POW35.len() == u64_power_limit(35) as usize + 1); +const _: () = assert!(SMALL_INT_POW35.len() > f64_mantissa_limit(35) as usize); +const _: () = assert!(SMALL_INT_POW35.len() == u64_power_limit(35) as usize + 1); /// Pre-computed, small powers-of-35. pub const SMALL_F32_POW35: [f32; 5] = [1.0, 35.0, 1225.0, 42875.0, 1500625.0]; -const_assert!(SMALL_F32_POW35.len() > f32_exponent_limit(35).1 as usize); +const _: () = assert!(SMALL_F32_POW35.len() > f32_exponent_limit(35).1 as usize); /// Pre-computed, small powers-of-35. pub const SMALL_F64_POW35: [f64; 11] = [ @@ -2431,7 +2430,7 @@ pub const SMALL_F64_POW35: [f64; 11] = [ 78815638671875.0, 2758547353515625.0, ]; -const_assert!(SMALL_F64_POW35.len() > f64_exponent_limit(35).1 as usize); +const _: () = assert!(SMALL_F64_POW35.len() > f64_exponent_limit(35).1 as usize); /// Pre-computed large power-of-35 for 32-bit limbs. #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] @@ -2469,13 +2468,13 @@ pub const SMALL_INT_POW36: [u64; 13] = [ 131621703842267136, 4738381338321616896, ]; -const_assert!(SMALL_INT_POW36.len() > f64_mantissa_limit(36) as usize); -const_assert!(SMALL_INT_POW36.len() == u64_power_limit(36) as usize + 1); +const _: () = assert!(SMALL_INT_POW36.len() > f64_mantissa_limit(36) as usize); +const _: () = assert!(SMALL_INT_POW36.len() == u64_power_limit(36) as usize + 1); /// Pre-computed, small powers-of-36. pub const SMALL_F32_POW36: [f32; 8] = [1.0, 36.0, 1296.0, 46656.0, 1679616.0, 60466176.0, 2176782336.0, 78364164096.0]; -const_assert!(SMALL_F32_POW36.len() > f32_exponent_limit(36).1 as usize); +const _: () = assert!(SMALL_F32_POW36.len() > f32_exponent_limit(36).1 as usize); /// Pre-computed, small powers-of-36. pub const SMALL_F64_POW36: [f64; 17] = [ @@ -2497,4 +2496,4 @@ pub const SMALL_F64_POW36: [f64; 17] = [ 2.2107391972073336e+23, 7.958661109946401e+24, ]; -const_assert!(SMALL_F64_POW36.len() > f64_exponent_limit(36).1 as usize); +const _: () = assert!(SMALL_F64_POW36.len() > f64_exponent_limit(36).1 as usize); diff --git a/lexical-parse-float/tests/api_tests.rs b/lexical-parse-float/tests/api_tests.rs index 0e11935e..3a015d85 100644 --- a/lexical-parse-float/tests/api_tests.rs +++ b/lexical-parse-float/tests/api_tests.rs @@ -21,20 +21,20 @@ fn special_bytes_test() { assert!(f32::from_lexical(b"INF").unwrap().is_infinite()); assert!(f32::from_lexical(b"Infinity").unwrap().is_infinite()); - let options = - Options::builder().nan_string(Some(b"nan")).inf_string(Some(b"Infinity")).build().unwrap(); + const OPTIONS: Options = + Options::builder().nan_string(Some(b"nan")).inf_string(Some(b"Infinity")).build_strict(); // The error message depends on whether the radix feature is enabled. - assert!(f32::from_lexical_with_options::(b"inf", &options).is_err()); - assert!(f32::from_lexical_with_options::(b"Infinity", &options).unwrap().is_infinite()); + assert!(f32::from_lexical_with_options::(b"inf", &OPTIONS).is_err()); + assert!(f32::from_lexical_with_options::(b"Infinity", &OPTIONS).unwrap().is_infinite()); } #[test] #[cfg(feature = "power-of-two")] fn invalid_format_test() { const FORMAT: u128 = NumberFormatBuilder::from_radix(40); - let options = Options::new(); - let res = f32::from_lexical_with_options::(b"inf", &options); + const OPTIONS: Options = Options::new(); + let res = f32::from_lexical_with_options::(b"inf", &OPTIONS); assert!(res.is_err()); assert_eq!(res, Err(Error::InvalidMantissaRadix)); } @@ -46,9 +46,9 @@ fn invalid_punctuation_test() { .digit_separator(num::NonZeroU8::new(b'h')) .base_prefix(num::NonZeroU8::new(b'h')) .integer_internal_digit_separator(true) - .build(); - let options = Options::new(); - let res = f32::from_lexical_with_options::(b"inf", &options); + .build_unchecked(); + const OPTIONS: Options = Options::new(); + let res = f32::from_lexical_with_options::(b"inf", &OPTIONS); assert!(res.is_err()); assert_eq!(res, Err(Error::InvalidPunctuation)); } @@ -139,35 +139,35 @@ fn f32_decimal_test() { #[cfg(feature = "radix")] fn f32_radix_test() { const BASE36: u128 = NumberFormatBuilder::from_radix(36); - let options = Options::builder().exponent(b'^').build().unwrap(); - assert_eq!(1234.0, f32::from_lexical_with_options::(b"YA", &options).unwrap()); - let options = options.rebuild().lossy(true).build().unwrap(); - assert_eq!(1234.0, f32::from_lexical_with_options::(b"YA", &options).unwrap()); + const CUSTOM: Options = Options::builder().exponent(b'^').build_strict(); + assert_eq!(1234.0, f32::from_lexical_with_options::(b"YA", &CUSTOM).unwrap()); + const OPTIONS: Options = CUSTOM.rebuild().lossy(true).build_strict(); + assert_eq!(1234.0, f32::from_lexical_with_options::(b"YA", &OPTIONS).unwrap()); const BASE21: u128 = NumberFormatBuilder::from_radix(21); assert_eq!( 2879628700000000000000000.0, - f32::from_lexical_with_options::(b"4.BHJ97^I", &options).unwrap() + f32::from_lexical_with_options::(b"4.BHJ97^I", &OPTIONS).unwrap() ); assert_eq!( 48205230000000000000000000000000000000.0, - f32::from_lexical_with_options::(b"4.C4407^17", &options).unwrap() + f32::from_lexical_with_options::(b"4.C4407^17", &OPTIONS).unwrap() ); assert_eq!( 105861930000000000000000000000000000000.0, - f32::from_lexical_with_options::(b"A.15A^17", &options).unwrap() + f32::from_lexical_with_options::(b"A.15A^17", &OPTIONS).unwrap() ); assert_eq!( 63900540000000000000000000000000000000.0, - f32::from_lexical_with_options::(b"6.1AK^17", &options).unwrap() + f32::from_lexical_with_options::(b"6.1AK^17", &OPTIONS).unwrap() ); assert_eq!( 48205210000000000000000000000000000000.0, - f32::from_lexical_with_options::(b"4.C44^17", &options).unwrap() + f32::from_lexical_with_options::(b"4.C44^17", &OPTIONS).unwrap() ); assert_eq!( 48205230000000000000000000000000000000.0, - f32::from_lexical_with_options::(b"4C440700000000000000000000000.0", &options) + f32::from_lexical_with_options::(b"4C440700000000000000000000000.0", &OPTIONS) .unwrap() ); } @@ -241,8 +241,8 @@ fn parse_f64_test() { #[cfg(feature = "power-of-two")] let parse_binary = move |x| { const BINARY: u128 = NumberFormatBuilder::from_radix(2); - let options = Options::builder().exponent(b'^').build().unwrap(); - f64::from_lexical_partial_with_options::(x, &options) + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); + f64::from_lexical_partial_with_options::(x, &OPTIONS) }; assert_eq!(Ok((0.0, 1)), parse(b"0")); @@ -530,10 +530,10 @@ fn f64_decimal_test() { #[cfg(feature = "radix")] fn f64_radix_test() { const FORMAT: u128 = NumberFormatBuilder::from_radix(36); - let options = Options::builder().exponent(b'^').build().unwrap(); - assert_eq!(1234.0, f64::from_lexical_with_options::(b"YA", &options).unwrap()); - let options = options.rebuild().lossy(true).build().unwrap(); - assert_eq!(1234.0, f64::from_lexical_with_options::(b"YA", &options).unwrap()); + const CUSTOM: Options = Options::builder().exponent(b'^').build_strict(); + assert_eq!(1234.0, f64::from_lexical_with_options::(b"YA", &CUSTOM).unwrap()); + const OPTIONS: Options = CUSTOM.rebuild().lossy(true).build_strict(); + assert_eq!(1234.0, f64::from_lexical_with_options::(b"YA", &OPTIONS).unwrap()); } #[test] @@ -551,8 +551,8 @@ fn parse_binary_f64_test() { #[cfg(feature = "power-of-two")] let parse_binary = move |x| { const BINARY: u128 = NumberFormatBuilder::from_radix(2); - let options = Options::builder().exponent(b'^').build().unwrap(); - f64::from_lexical_partial_with_options::(x, &options) + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); + f64::from_lexical_partial_with_options::(x, &OPTIONS) }; // Test a wide variety of denormal floats here. @@ -647,8 +647,8 @@ fn parse_binary_f64_test() { #[test] fn parse_f32_lossy_test() { const FORMAT: u128 = STANDARD; - let options = Options::builder().lossy(true).build().unwrap(); - let parse = move |x| f32::from_lexical_partial_with_options::(x, &options); + const OPTIONS: Options = Options::builder().lossy(true).build_strict(); + let parse = move |x| f32::from_lexical_partial_with_options::(x, &OPTIONS); assert_eq!(Ok((1.2345, 6)), parse(b"1.2345")); assert_eq!(Ok((12.345, 6)), parse(b"12.345")); @@ -659,8 +659,8 @@ fn parse_f32_lossy_test() { #[test] fn parse_f64_lossy_test() { const FORMAT: u128 = STANDARD; - let options = Options::builder().lossy(true).build().unwrap(); - let parse = move |x| f64::from_lexical_partial_with_options::(x, &options); + const OPTIONS: Options = Options::builder().lossy(true).build_strict(); + let parse = move |x| f64::from_lexical_partial_with_options::(x, &OPTIONS); assert_eq!(Ok((1.2345, 6)), parse(b"1.2345")); assert_eq!(Ok((12.345, 6)), parse(b"12.345")); @@ -672,22 +672,22 @@ fn parse_f64_lossy_test() { fn f32_lossy_decimal_test() { const FORMAT: u128 = STANDARD; - let options = Options::builder().lossy(true).build().unwrap(); + const OPTIONS: Options = Options::builder().lossy(true).build_strict(); assert_eq!( Err(Error::EmptyMantissa(1)), - f32::from_lexical_with_options::(b".", &options) + f32::from_lexical_with_options::(b".", &OPTIONS) ); - assert_eq!(Err(Error::Empty(0)), f32::from_lexical_with_options::(b"", &options)); - assert_eq!(Ok(0.0), f32::from_lexical_with_options::(b"0.0", &options)); + assert_eq!(Err(Error::Empty(0)), f32::from_lexical_with_options::(b"", &OPTIONS)); + assert_eq!(Ok(0.0), f32::from_lexical_with_options::(b"0.0", &OPTIONS)); assert_eq!( Err((Error::InvalidDigit(1)).into()), - f32::from_lexical_with_options::(b"1a", &options) + f32::from_lexical_with_options::(b"1a", &OPTIONS) ); // Bug fix for Issue #8 assert_eq!( Ok(5.002868148396374), - f32::from_lexical_with_options::(b"5.002868148396374", &options) + f32::from_lexical_with_options::(b"5.002868148396374", &OPTIONS) ); } @@ -695,22 +695,22 @@ fn f32_lossy_decimal_test() { fn f64_lossy_decimal_test() { const FORMAT: u128 = STANDARD; - let options = Options::builder().lossy(true).build().unwrap(); + const OPTIONS: Options = Options::builder().lossy(true).build_strict(); assert_eq!( Err(Error::EmptyMantissa(1)), - f64::from_lexical_with_options::(b".", &options) + f64::from_lexical_with_options::(b".", &OPTIONS) ); - assert_eq!(Err(Error::Empty(0)), f64::from_lexical_with_options::(b"", &options)); - assert_eq!(Ok(0.0), f64::from_lexical_with_options::(b"0.0", &options)); + assert_eq!(Err(Error::Empty(0)), f64::from_lexical_with_options::(b"", &OPTIONS)); + assert_eq!(Ok(0.0), f64::from_lexical_with_options::(b"0.0", &OPTIONS)); assert_eq!( Err((Error::InvalidDigit(1)).into()), - f64::from_lexical_with_options::(b"1a", &options) + f64::from_lexical_with_options::(b"1a", &OPTIONS) ); // Bug fix for Issue #8 assert_eq!( Ok(5.002868148396374), - f64::from_lexical_with_options::(b"5.002868148396374", &options) + f64::from_lexical_with_options::(b"5.002868148396374", &OPTIONS) ); } @@ -725,63 +725,63 @@ fn f64_special_test() { // Comments match (no_special, case_sensitive, has_sep) const F1: u128 = STANDARD; const F2: u128 = format::IGNORE; - const F3: u128 = rebuild(F1).no_special(true).build(); - const F4: u128 = rebuild(F1).case_sensitive_special(true).build(); - const F5: u128 = rebuild(F2).case_sensitive_special(true).build(); + const F3: u128 = rebuild(F1).no_special(true).build_strict(); + const F4: u128 = rebuild(F1).case_sensitive_special(true).build_strict(); + const F5: u128 = rebuild(F2).case_sensitive_special(true).build_strict(); - let opts = Options::new(); + const OPTIONS: Options = Options::new(); // Easy NaN - assert!(f64::from_lexical_with_options::(b"NaN", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"NaN", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"NaN", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"NaN", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"NaN", &opts).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"NaN", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"NaN", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"NaN", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"NaN", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"NaN", &OPTIONS).unwrap().is_nan()); // Case-sensitive NaN. - assert!(f64::from_lexical_with_options::(b"nan", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"nan", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"nan", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"nan", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"nan", &opts).is_err()); + assert!(f64::from_lexical_with_options::(b"nan", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"nan", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"nan", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"nan", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"nan", &OPTIONS).is_err()); // Digit-separator NaN. - assert!(f64::from_lexical_with_options::(b"N_aN", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"N_aN", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"N_aN", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"N_aN", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"N_aN", &opts).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"N_aN", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"N_aN", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"N_aN", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"N_aN", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"N_aN", &OPTIONS).unwrap().is_nan()); // Digit-separator + case-sensitive NaN. - assert!(f64::from_lexical_with_options::(b"n_an", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_an", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"n_an", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_an", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_an", &opts).is_err()); + assert!(f64::from_lexical_with_options::(b"n_an", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_an", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"n_an", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_an", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_an", &OPTIONS).is_err()); // Leading digit separator + case-sensitive NaN. - assert!(f64::from_lexical_with_options::(b"_n_a_n", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"_n_a_n", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"_n_a_n", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"_n_a_n", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"_n_a_n", &opts).is_err()); + assert!(f64::from_lexical_with_options::(b"_n_a_n", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"_n_a_n", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"_n_a_n", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"_n_a_n", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"_n_a_n", &OPTIONS).is_err()); // Trailing digit separator + case-sensitive NaN. - assert!(f64::from_lexical_with_options::(b"n_a_n_", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_a_n_", &opts).unwrap().is_nan()); - assert!(f64::from_lexical_with_options::(b"n_a_n_", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_a_n_", &opts).is_err()); - assert!(f64::from_lexical_with_options::(b"n_a_n_", &opts).is_err()); + assert!(f64::from_lexical_with_options::(b"n_a_n_", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_a_n_", &OPTIONS).unwrap().is_nan()); + assert!(f64::from_lexical_with_options::(b"n_a_n_", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_a_n_", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"n_a_n_", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn case_sensitive_exponent_test() { - const FORMAT: u128 = NumberFormatBuilder::new().case_sensitive_exponent(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e+300", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0E+300", &options).is_err()); + const FORMAT: u128 = NumberFormatBuilder::new().case_sensitive_exponent(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e+300", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0E+300", &OPTIONS).is_err()); assert!(f64::from_lexical(b"+3.0e+300").is_ok()); assert!(f64::from_lexical(b"+3.0E+300").is_ok()); @@ -790,138 +790,138 @@ fn case_sensitive_exponent_test() { #[test] #[cfg(feature = "format")] fn f64_required_integer_digits_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_integer_digits(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b".0", &options).is_err()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_integer_digits(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b".0", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn f64_required_fraction_digits_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_fraction_digits(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_fraction_digits(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_required_digits_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_digits(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b".0", &options).is_err()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_digits(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b".0", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn f64_no_positive_mantissa_sign_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).no_positive_mantissa_sign(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"-3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).no_positive_mantissa_sign(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"-3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_required_mantissa_sign_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_mantissa_sign(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"-3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0", &options).is_err()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_mantissa_sign(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"-3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn f64_no_exponent_notation_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).no_exponent_notation(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3.0e-7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3e", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3e-", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).no_exponent_notation(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3.0e-7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3e", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3e-", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_optional_exponent_test() { const FORMAT: u128 = format::PERMISSIVE; - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e-7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e-", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e-7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e-", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_required_exponent_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_exponent_digits(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e-7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3.0e-", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_exponent_digits(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e-7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3.0e-", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_no_positive_exponent_sign_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).no_positive_exponent_sign(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0e+7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3.0e-7", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).no_positive_exponent_sign(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0e+7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3.0e-7", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_required_exponent_sign_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).required_exponent_sign(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3.0e+7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.0e-7", &options).is_ok()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).required_exponent_sign(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3.0e+7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.0e-7", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn f64_no_exponent_without_fraction_test() { - const F1: u128 = rebuild(format::PERMISSIVE).no_exponent_without_fraction(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3e7", &options).is_err()); - - const F2: u128 = rebuild(F1).required_fraction_digits(true).build(); - assert!(f64::from_lexical_with_options::(b"3.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"3.e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3e7", &options).is_err()); + const F1: u128 = rebuild(format::PERMISSIVE).no_exponent_without_fraction(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3e7", &OPTIONS).is_err()); + + const F2: u128 = rebuild(F1).required_fraction_digits(true).build_strict(); + assert!(f64::from_lexical_with_options::(b"3.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"3.e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3e7", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn f64_no_leading_zeros_test() { - const FORMAT: u128 = rebuild(format::PERMISSIVE).no_float_leading_zeros(true).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"1.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"0.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"01.0", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"10.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"010.0", &options).is_err()); + const FORMAT: u128 = rebuild(format::PERMISSIVE).no_float_leading_zeros(true).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"1.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"0.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"01.0", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"10.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"010.0", &OPTIONS).is_err()); } #[test] @@ -930,11 +930,11 @@ fn f64_required_exponent_notation_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .required_exponent_digits(false) .required_exponent_notation(true) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"3.0e", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"0.e", &options).is_ok()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"3.0e", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"0.e", &OPTIONS).is_ok()); } #[test] @@ -943,11 +943,11 @@ fn f64_integer_internal_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .integer_internal_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3_1.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"_31.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31_.0e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3_1.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"_31.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31_.0e7", &OPTIONS).is_err()); } #[test] @@ -956,11 +956,11 @@ fn f64_fraction_internal_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .fraction_internal_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.0_1e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31._01e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01_e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.0_1e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31._01e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01_e7", &OPTIONS).is_err()); } #[test] @@ -969,11 +969,11 @@ fn f64_exponent_internal_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .exponent_internal_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.01e7_1", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31.01e_71", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01e71_", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.01e7_1", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31.01e_71", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01e71_", &OPTIONS).is_err()); } #[test] @@ -982,11 +982,11 @@ fn f64_integer_leading_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .integer_leading_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3_1.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"_31.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31_.0e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3_1.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"_31.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31_.0e7", &OPTIONS).is_err()); } #[test] @@ -995,11 +995,11 @@ fn f64_fraction_leading_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .fraction_leading_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.0_1e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31._01e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31.01_e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.0_1e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31._01e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31.01_e7", &OPTIONS).is_err()); } #[test] @@ -1008,11 +1008,11 @@ fn f64_exponent_leading_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .exponent_leading_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.01e7_1", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01e_71", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31.01e71_", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.01e7_1", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01e_71", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31.01e71_", &OPTIONS).is_err()); } #[test] @@ -1021,11 +1021,11 @@ fn f64_integer_trailing_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .integer_trailing_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3_1.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"_31.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31_.0e7", &options).is_ok()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3_1.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"_31.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31_.0e7", &OPTIONS).is_ok()); } #[test] @@ -1034,11 +1034,11 @@ fn f64_fraction_trailing_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .fraction_trailing_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.0_1e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31._01e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01_e7", &options).is_ok()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.0_1e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31._01e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01_e7", &OPTIONS).is_ok()); } #[test] @@ -1047,11 +1047,11 @@ fn f64_exponent_trailing_digit_separator_test() { const FORMAT: u128 = rebuild(format::PERMISSIVE) .exponent_trailing_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.01e7_1", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01e_71", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01e71_", &options).is_ok()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.01e7_1", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01e_71", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01e71_", &OPTIONS).is_ok()); } #[test] @@ -1061,11 +1061,11 @@ fn f64_integer_consecutive_digit_separator_test() { .integer_internal_digit_separator(true) .integer_consecutive_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"3__1.0e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"_31.0e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31_.0e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"3__1.0e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"_31.0e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31_.0e7", &OPTIONS).is_err()); } #[test] @@ -1075,11 +1075,11 @@ fn f64_fraction_consecutive_digit_separator_test() { .fraction_internal_digit_separator(true) .fraction_consecutive_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.0__1e7", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31._01e7", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01_e7", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.0__1e7", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31._01e7", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01_e7", &OPTIONS).is_err()); } #[test] @@ -1089,11 +1089,11 @@ fn f64_exponent_consecutive_digit_separator_test() { .exponent_internal_digit_separator(true) .exponent_consecutive_digit_separator(true) .digit_separator(num::NonZeroU8::new(b'_')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"31.01e7__1", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"31.01e_71", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"31.01e71_", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"31.01e7__1", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"31.01e_71", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"31.01e71_", &OPTIONS).is_err()); } #[test] @@ -1102,17 +1102,17 @@ fn f64_json_exponent_without_dot() { // Tests courtesy of @ijl: // https://github.com/Alexhuszagh/rust-lexical/issues/24#issuecomment-578153783 const FORMAT: u128 = format::JSON; - let options = Options::new(); + const OPTIONS: Options = Options::new(); // JSONTestSuite/test_parsing/y_number_0e1.json - assert!(f64::from_lexical_with_options::(b"0e1", &options).is_ok()); + assert!(f64::from_lexical_with_options::(b"0e1", &OPTIONS).is_ok()); // JSONTestSuite/test_parsing/y_number_int_with_exp.json - assert!(f64::from_lexical_with_options::(b"20e1", &options).is_ok()); + assert!(f64::from_lexical_with_options::(b"20e1", &OPTIONS).is_ok()); // JSONTestSuite/test_parsing/y_number_real_capital_e_pos_exp.json - assert!(f64::from_lexical_with_options::(b"1E+2", &options).is_ok()); + assert!(f64::from_lexical_with_options::(b"1E+2", &OPTIONS).is_ok()); // JSONTestSuite/test_transform/number_1e-999.json - assert!(f64::from_lexical_with_options::(b"1E-999", &options).is_ok()); + assert!(f64::from_lexical_with_options::(b"1E-999", &OPTIONS).is_ok()); // nativejson-benchmark/data/jsonchecker/pass01.json - assert!(f64::from_lexical_with_options::(b"23456789012E66", &options).is_ok()); + assert!(f64::from_lexical_with_options::(b"23456789012E66", &OPTIONS).is_ok()); } #[test] @@ -1121,73 +1121,75 @@ fn f64_json_exponent_requires_digit() { // Tests courtesy of @ijl: // https://github.com/Alexhuszagh/rust-lexical/issues/24#issuecomment-578153783 const FORMAT: u128 = format::JSON; - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"1e", &options).is_err()); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"1e", &OPTIONS).is_err()); // JSONTestSuite/test_parsing/n_number_9.e+.json - assert!(f64::from_lexical_with_options::(b"9.e+", &options).is_err()); + assert!(f64::from_lexical_with_options::(b"9.e+", &OPTIONS).is_err()); // JSONTestSuite/test_parsing/n_number_2.e-3.json - assert!(f64::from_lexical_with_options::(b"2.e-3", &options).is_err()); + assert!(f64::from_lexical_with_options::(b"2.e-3", &OPTIONS).is_err()); // JSONTestSuite/test_parsing/n_number_real_without_fractional_part.json - assert!(f64::from_lexical_with_options::(b"1.", &options).is_err()); + assert!(f64::from_lexical_with_options::(b"1.", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn f64_json_no_leading_zero() { const FORMAT: u128 = format::JSON; - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"12.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"-12.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"012.0", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"-012.0", &options).is_err()); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"12.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"-12.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"012.0", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"-012.0", &OPTIONS).is_err()); } #[test] #[cfg(all(feature = "power-of-two", feature = "format"))] fn base_prefix_test() { - const FORMAT: u128 = NumberFormatBuilder::new().base_prefix(num::NonZeroU8::new(b'x')).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+0x", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+0x ", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+0x3", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3.0e+300", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3.0e+300", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3.0e+300 ", &options).is_err()); - - assert!(f64::from_lexical_partial_with_options::(b"+0x", &options).is_err()); - assert!(f64::from_lexical_partial_with_options::(b"+0x ", &options).is_err()); - assert!(f64::from_lexical_partial_with_options::(b"+0x3", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300 ", &options).is_ok()); + const FORMAT: u128 = + NumberFormatBuilder::new().base_prefix(num::NonZeroU8::new(b'x')).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+0x", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+0x ", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+0x3", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3.0e+300", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3.0e+300", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3.0e+300 ", &OPTIONS).is_err()); + + assert!(f64::from_lexical_partial_with_options::(b"+0x", &OPTIONS).is_err()); + assert!(f64::from_lexical_partial_with_options::(b"+0x ", &OPTIONS).is_err()); + assert!(f64::from_lexical_partial_with_options::(b"+0x3", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+0x3.0e+300 ", &OPTIONS).is_ok()); } #[test] #[cfg(all(feature = "power-of-two", feature = "format"))] fn base_suffix_test() { - const FORMAT: u128 = NumberFormatBuilder::new().base_suffix(num::NonZeroU8::new(b'h')).build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"h", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"-h ", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+h ", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+3h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e+300h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e+300h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+3.0e+300h ", &options).is_err()); - - assert!(f64::from_lexical_partial_with_options::(b"+h", &options).is_err()); - assert!(f64::from_lexical_partial_with_options::(b"+h ", &options).is_err()); - assert!(f64::from_lexical_partial_with_options::(b"+3h", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0h", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h", &options).is_ok()); - assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h ", &options).is_ok()); + const FORMAT: u128 = + NumberFormatBuilder::new().base_suffix(num::NonZeroU8::new(b'h')).build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"h", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"-h ", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+h ", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+3h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e+300h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e+300h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+3.0e+300h ", &OPTIONS).is_err()); + + assert!(f64::from_lexical_partial_with_options::(b"+h", &OPTIONS).is_err()); + assert!(f64::from_lexical_partial_with_options::(b"+h ", &OPTIONS).is_err()); + assert!(f64::from_lexical_partial_with_options::(b"+3h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_partial_with_options::(b"+3.0e+300h ", &OPTIONS).is_ok()); } #[test] @@ -1196,14 +1198,14 @@ fn base_prefix_and_suffix_test() { const FORMAT: u128 = NumberFormatBuilder::new() .base_prefix(num::NonZeroU8::new(b'x')) .base_suffix(num::NonZeroU8::new(b'h')) - .build(); - let options = Options::new(); - assert!(f64::from_lexical_with_options::(b"+3h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0xh", &options).is_err()); - assert!(f64::from_lexical_with_options::(b"+0x3.0e+300h", &options).is_ok()); - assert!(f64::from_lexical_with_options::(b"+0x3.0e+300h ", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(f64::from_lexical_with_options::(b"+3h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0xh", &OPTIONS).is_err()); + assert!(f64::from_lexical_with_options::(b"+0x3.0e+300h", &OPTIONS).is_ok()); + assert!(f64::from_lexical_with_options::(b"+0x3.0e+300h ", &OPTIONS).is_err()); } #[test] @@ -1213,23 +1215,23 @@ fn issue66_test() { const JSON: u128 = format::JSON; const CXX: u128 = format::CXX17_LITERAL; - let options = Options::new(); + const OPTIONS: Options = Options::new(); - assert_eq!(f64::from_lexical_with_options::(b"42.0", &options), Ok(42.0)); - assert_eq!(f64::from_lexical_with_options::(b"42.0", &options), Ok(42.0)); - assert_eq!(f64::from_lexical_with_options::(b"4_2.0", &options), Ok(42.0)); - assert_eq!(f64::from_lexical_with_options::(b"42.0", &options), Ok(42.0)); - assert_eq!(f64::from_lexical_with_options::(b"4'2.0", &options), Ok(42.0)); + assert_eq!(f64::from_lexical_with_options::(b"42.0", &OPTIONS), Ok(42.0)); + assert_eq!(f64::from_lexical_with_options::(b"42.0", &OPTIONS), Ok(42.0)); + assert_eq!(f64::from_lexical_with_options::(b"4_2.0", &OPTIONS), Ok(42.0)); + assert_eq!(f64::from_lexical_with_options::(b"42.0", &OPTIONS), Ok(42.0)); + assert_eq!(f64::from_lexical_with_options::(b"4'2.0", &OPTIONS), Ok(42.0)); } #[test] #[cfg(feature = "power-of-two")] fn issue68_test() { const FORMAT: u128 = NumberFormatBuilder::from_radix(16); - let options = Options::builder().exponent(b'^').build().unwrap(); + const OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); // Roughly 2^1375, 15 1s. let hex = b"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"; - assert_eq!(f32::INFINITY, f32::from_lexical_with_options::(hex, &options).unwrap()); - assert_eq!(f64::INFINITY, f64::from_lexical_with_options::(hex, &options).unwrap()); + assert_eq!(f32::INFINITY, f32::from_lexical_with_options::(hex, &OPTIONS).unwrap()); + assert_eq!(f64::INFINITY, f64::from_lexical_with_options::(hex, &OPTIONS).unwrap()); } diff --git a/lexical-parse-float/tests/issue_96_tests.rs b/lexical-parse-float/tests/issue_96_tests.rs index 4203cf21..07ab3da7 100644 --- a/lexical-parse-float/tests/issue_96_tests.rs +++ b/lexical-parse-float/tests/issue_96_tests.rs @@ -13,423 +13,423 @@ use lexical_util::format::STANDARD; #[test] fn issue_96_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const NO_CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); const CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); const NO_LEADING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(false) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); let result = f64::from_lexical(b"_-1234"); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = f64::from_lexical_with_options::(b"_-1234", &opts); + let result = f64::from_lexical_with_options::(b"_-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(1))); - let result = f64::from_lexical_with_options::(b"^-1234", &opts); + let result = f64::from_lexical_with_options::(b"^-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); // NOTE: This uis correct, since it's "trailing" - let result = f64::from_lexical_with_options::(b"_-1234", &opts); + let result = f64::from_lexical_with_options::(b"_-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(1))); - let result = f64::from_lexical_with_options::(b"_1234", &opts); + let result = f64::from_lexical_with_options::(b"_1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = f64::from_lexical_with_options::(b"X1234", &opts); + let result = f64::from_lexical_with_options::(b"X1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = f64::from_lexical_with_options::(b"__1__234__", &opts); + let result = f64::from_lexical_with_options::(b"__1__234__", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = f64::from_lexical_with_options::(b"__1__234__", &opts); + let result = f64::from_lexical_with_options::(b"__1__234__", &OPTS); assert_eq!(result, Ok(1234f64)); } #[test] fn issue_96_i_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(0))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"_1_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(0))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(0))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_23", &OPTS); assert_eq!(result, Ok((1123f64, 6))); - let result = f64::from_lexical_partial_with_options::(b"1_1__23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1__23", &OPTS); assert_eq!(result, Ok((11f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1_1_23_", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_23_", &OPTS); assert_eq!(result, Ok((1123f64, 6))); - let result = f64::from_lexical_partial_with_options::(b"1_1_23.", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_23.", &OPTS); assert_eq!(result, Ok((1123f64, 7))); } #[test] fn issue_96_l_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1f64, 2))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); } #[test] fn issue_96_t_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(0))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123f64, 5))); - let result = f64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123f64, 4))); } #[test] fn issue_96_il_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .leading_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((123f64, 5))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((123f64, 6))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((11f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123f64, 4))); - let result = f64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123f64, 4))); } #[test] fn issue_96_it_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(0))); let result: Result<(f64, usize), Error> = - f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((11f64, 4))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123f64, 5))); - let result = f64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123f64, 4))); } #[test] fn issue_96_lt_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1f64, 2))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"_11_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_11_", &OPTS); assert_eq!(result, Ok((11f64, 4))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::EmptyMantissa(1))); - let result = f64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123f64, 5))); - let result = f64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123f64, 4))); } #[test] fn issue_96_no_required_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .required_digits(false) - .build(); + .build_strict(); - let result = f64::from_lexical_partial_with_options::(b"", &opts); + let result = f64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Ok((0f64, 0))); - let result = f64::from_lexical_partial_with_options::(b"_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Ok((0f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"+_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Ok((0f64, 2))); - let result = f64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1f64, 2))); - let result = f64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1f64, 3))); - let result = f64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = f64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((1f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"_11_", &opts); + let result = f64::from_lexical_partial_with_options::(b"_11_", &OPTS); assert_eq!(result, Ok((11f64, 4))); - let result = f64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = f64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Ok((0f64, 1))); - let result = f64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123f64, 5))); - let result = f64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = f64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123f64, 4))); } #[test] fn issue_96_rounding_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); let input = b"0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225073858507200889024586876085859887650423112240959465493524802562440009228235695178775888803759155264230978095043431208587738715835729182199302029437922422355981982750124204178896957131179108226104397197960400045489739193807919893608152561311337614984204327175103362739154978273159414382813627511383860409424946494228631669542910508020181592664213499660651780309507591305871984642390606863710200510872328278467884363194451586613504122347901479236958520832159762106637540161373658304419360371477835530668283453563400507407304013560296804637591858316312422452159926254649430083685186171942241764645513713542013221703137049658321015465406803539741790602258950302350193751977303094576317321085250729930508976158251915"; - let result = f32::from_lexical_partial_with_options::(input, &opts); + let result = f32::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((0f32, input.len()))); - let result = f32::from_lexical_partial_with_options::(input, &opts); + let result = f32::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((0f32, input.len()))); - let result = f64::from_lexical_partial_with_options::(input, &opts); + let result = f64::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((2.225073858507201e-308f64, input.len()))); - let result = f64::from_lexical_partial_with_options::(input, &opts); + let result = f64::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((2.225073858507201e-308f64, input.len()))); let input = b"_0e+___00"; - let result = f32::from_lexical_partial_with_options::(input, &opts); + let result = f32::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((0f32, input.len()))); - let result = f32::from_lexical_with_options::(input, &opts); + let result = f32::from_lexical_with_options::(input, &OPTS); assert_eq!(result, Ok(0f32)); - let result = f64::from_lexical_partial_with_options::(input, &opts); + let result = f64::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((0f64, input.len()))); - let result = f64::from_lexical_with_options::(input, &opts); + let result = f64::from_lexical_with_options::(input, &OPTS); assert_eq!(result, Ok(0f64)); let input = b"323081493377685546875e-297"; - let result = f64::from_lexical_with_options::(input, &opts); + let result = f64::from_lexical_with_options::(input, &OPTS); assert_eq!(result, Ok(3.2308149337768557e-277)); let input = b"32308_1493_3776_8554_6875e-297"; - let result = f64::from_lexical_with_options::(input, &opts); + let result = f64::from_lexical_with_options::(input, &OPTS); assert_eq!(result, Ok(3.2308149337768557e-277)); } #[test] fn issue_96_wuff_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); let input = b"0.000061094760894775390625"; - let result = f32::from_lexical_partial_with_options::(input, &opts); + let result = f32::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((6.109476e-5f32, input.len()))); - let result = f64::from_lexical_partial_with_options::(input, &opts); + let result = f64::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((6.109476089477539e-5, input.len()))); let input = b"0_.0000610_9476_0894775390_625"; - let result = f32::from_lexical_partial_with_options::(input, &opts); + let result = f32::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((6.109476e-5f32, input.len()))); - let result = f64::from_lexical_partial_with_options::(input, &opts); + let result = f64::from_lexical_partial_with_options::(input, &OPTS); assert_eq!(result, Ok((6.109476089477539e-5, input.len()))); } diff --git a/lexical-parse-float/tests/issue_98_tests.rs b/lexical-parse-float/tests/issue_98_tests.rs index ee137b83..b081c713 100644 --- a/lexical-parse-float/tests/issue_98_tests.rs +++ b/lexical-parse-float/tests/issue_98_tests.rs @@ -15,7 +15,7 @@ fn issue_98_test() { .no_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(true) - .build(); + .build_strict(); let result = f64::from_lexical_with_options::(b"1.1.0", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(3)); diff --git a/lexical-parse-float/tests/options_tests.rs b/lexical-parse-float/tests/options_tests.rs index 0a35500d..9cbb45c2 100644 --- a/lexical-parse-float/tests/options_tests.rs +++ b/lexical-parse-float/tests/options_tests.rs @@ -111,6 +111,7 @@ fn builder_test() { } #[test] +#[allow(deprecated)] fn options_test() { let mut opts = Options::new(); diff --git a/lexical-parse-float/tests/parse_tests.rs b/lexical-parse-float/tests/parse_tests.rs index 2bbd8659..b0ad19b7 100644 --- a/lexical-parse-float/tests/parse_tests.rs +++ b/lexical-parse-float/tests/parse_tests.rs @@ -7,78 +7,78 @@ use lexical_util::step::u64_step; #[test] fn parse_complete_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; - let result = parse::parse_complete::(string, &options); + let result = parse::parse_complete::(string, &OPTIONS); assert_eq!(result, Ok(1.2345e10)); let string = b"1.2345e"; - let result = parse::parse_complete::(string, &options); + let result = parse::parse_complete::(string, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; - let result = parse::parse_complete::(string, &options); + let result = parse::parse_complete::(string, &OPTIONS); assert!(result.is_err()); } #[test] fn fast_path_complete_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; - let result = parse::fast_path_complete::(string, &options); + let result = parse::fast_path_complete::(string, &OPTIONS); assert_eq!(result, Ok(1.2345e10)); let string = b"1.2345e"; - let result = parse::fast_path_complete::(string, &options); + let result = parse::fast_path_complete::(string, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; - let result = parse::fast_path_complete::(string, &options); + let result = parse::fast_path_complete::(string, &OPTIONS); assert!(result.is_err()); } #[test] fn parse_partial_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; - let result = parse::parse_partial::(string, &options); + let result = parse::parse_partial::(string, &OPTIONS); assert_eq!(result, Ok((1.2345e10, 9))); let string = b"1.2345e"; - let result = parse::parse_partial::(string, &options); + let result = parse::parse_partial::(string, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; - let result = parse::parse_partial::(string, &options); + let result = parse::parse_partial::(string, &OPTIONS); assert_eq!(result, Ok((1.2345, 6))); } #[test] fn fast_path_partial_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; - let result = parse::fast_path_partial::(string, &options); + let result = parse::fast_path_partial::(string, &OPTIONS); assert_eq!(result, Ok((1.2345e10, 9))); let string = b"1.2345e"; - let result = parse::fast_path_partial::(string, &options); + let result = parse::fast_path_partial::(string, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; - let result = parse::fast_path_partial::(string, &options); + let result = parse::fast_path_partial::(string, &OPTIONS); assert_eq!(result, Ok((1.2345, 6))); } #[test] fn parse_number_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_complete_number(byte, false, &options); + let result = parse::parse_complete_number(byte, false, &OPTIONS); assert!(result.is_ok()); let num = result.unwrap(); assert_eq!(num.mantissa, 12345); @@ -87,22 +87,22 @@ fn parse_number_test() { let string = b"1.2345e"; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_complete_number(byte, false, &options); + let result = parse::parse_complete_number(byte, false, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_complete_number(byte, false, &options); + let result = parse::parse_complete_number(byte, false, &OPTIONS); assert!(result.is_err()); } #[test] fn parse_partial_number_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let string = b"1.2345e10"; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_number(byte, false, &options); + let result = parse::parse_partial_number(byte, false, &OPTIONS); assert!(result.is_ok()); let (num, count) = result.unwrap(); assert_eq!(num.mantissa, 12345); @@ -112,12 +112,12 @@ fn parse_partial_number_test() { let string = b"1.2345e"; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_number(byte, false, &options); + let result = parse::parse_partial_number(byte, false, &OPTIONS); assert!(result.is_err()); let string = b"1.2345 "; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_number(byte, false, &options); + let result = parse::parse_partial_number(byte, false, &OPTIONS); assert!(result.is_ok()); let (num, count) = result.unwrap(); assert_eq!(num.mantissa, 12345); @@ -128,7 +128,7 @@ fn parse_partial_number_test() { // Leading zeros let string = b"00000000000000000000001.2345 "; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_number(byte, false, &options); + let result = parse::parse_partial_number(byte, false, &OPTIONS); assert!(result.is_ok()); let (num, count) = result.unwrap(); assert_eq!(num.mantissa, 12345); @@ -139,7 +139,7 @@ fn parse_partial_number_test() { // Leading zeros let string = b"0.00000000000000000000012345 "; let byte = string.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_number(byte, false, &options); + let result = parse::parse_partial_number(byte, false, &OPTIONS); assert!(result.is_ok()); let (num, count) = result.unwrap(); assert_eq!(num.mantissa, 12345); @@ -211,28 +211,28 @@ fn is_special_eq_test() { fn parse_positive_special_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let digits = b"NaN"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_positive_special::(byte, &options).unwrap(); + let result = parse::parse_positive_special::(byte, &OPTIONS).unwrap(); assert_eq!(result.1, 3); assert!(f64::is_nan(result.0)); let digits = b"NaN1"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_positive_special::(byte, &options).unwrap(); + let result = parse::parse_positive_special::(byte, &OPTIONS).unwrap(); assert_eq!(result.1, 3); assert!(f64::is_nan(result.0)); let digits = b"inf"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_positive_special::(byte, &options).unwrap(); + let result = parse::parse_positive_special::(byte, &OPTIONS).unwrap(); assert_eq!(result.1, 3); assert!(f64::is_infinite(result.0)); let digits = b"in"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_positive_special::(byte, &options); + let result = parse::parse_positive_special::(byte, &OPTIONS); assert_eq!(result, None); } @@ -240,10 +240,10 @@ fn parse_positive_special_test() { fn parse_partial_special_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let digits = b"NaN"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_partial_special::(byte, true, &options).unwrap(); + let result = parse::parse_partial_special::(byte, true, &OPTIONS).unwrap(); assert_eq!(result.1, 3); assert!(f64::is_nan(result.0)); assert!(f64::is_sign_negative(result.0)); @@ -253,15 +253,15 @@ fn parse_partial_special_test() { fn parse_parse_special_test() { const FORMAT: u128 = STANDARD; - let options = Options::new(); + const OPTIONS: Options = Options::new(); let digits = b"NaN"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_special::(byte, true, &options).unwrap(); + let result = parse::parse_special::(byte, true, &OPTIONS).unwrap(); assert!(f64::is_nan(result)); assert!(f64::is_sign_negative(result)); let digits = b"NaN1"; let byte = digits.bytes::<{ FORMAT }>(); - let result = parse::parse_special::(byte, true, &options); + let result = parse::parse_special::(byte, true, &OPTIONS); assert_eq!(result, None); } diff --git a/lexical-parse-integer/Cargo.toml b/lexical-parse-integer/Cargo.toml index d285fbeb..1a1bcd74 100644 --- a/lexical-parse-integer/Cargo.toml +++ b/lexical-parse-integer/Cargo.toml @@ -10,7 +10,7 @@ name = "lexical-parse-integer" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "1.0.5" -rust-version = "1.61.0" +rust-version = "1.60.0" exclude = [ "assets/*", "docs/*", @@ -18,9 +18,6 @@ exclude = [ "cargo-timing*.html" ] -[dependencies] -static_assertions = "1" - [dependencies.lexical-util] version = "1.0.5" path = "../lexical-util" @@ -48,3 +45,4 @@ lint = ["lexical-util/lint"] [package.metadata.docs.rs] features = ["radix", "format"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-parse-integer/src/algorithm.rs b/lexical-parse-integer/src/algorithm.rs index ec67a59a..dcb8bdd8 100644 --- a/lexical-parse-integer/src/algorithm.rs +++ b/lexical-parse-integer/src/algorithm.rs @@ -4,8 +4,8 @@ //! to read multiple digits at-a-time with less multiplication instructions, //! as well as other optimizations to avoid unnecessary compile-time branching. //! -//! See [Algorithm.md](/docs/Algorithm.md) for a more detailed description of -//! the algorithm choice here. See [Benchmarks.md](/docs/Benchmarks.md) for +//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of +//! the algorithm choice here. See [Benchmarks.md](/docs/Benchmarks) for //! recent benchmark data. //! //! These allow implementations of partial and complete parsers @@ -184,6 +184,7 @@ macro_rules! fmt_invalid_digit { /// /// Returns if the value is negative, or any values detected when /// validating the input. +#[doc(hidden)] #[macro_export] macro_rules! parse_sign { ( diff --git a/lexical-parse-integer/src/api.rs b/lexical-parse-integer/src/api.rs index 40adf814..44e0c335 100644 --- a/lexical-parse-integer/src/api.rs +++ b/lexical-parse-integer/src/api.rs @@ -64,8 +64,8 @@ macro_rules! integer_from_lexical { )*) } -from_lexical! {} -from_lexical_with_options! {} +from_lexical!("lexical_parse_integer", 1234, u64, 4); +from_lexical_with_options!("lexical_parse_integer", 1234, u64, 4, Options); integer_from_lexical! { u8 u8 ; u16 u16 ; diff --git a/lexical-parse-integer/src/lib.rs b/lexical-parse-integer/src/lib.rs index 95b804b2..ebee0ed0 100644 --- a/lexical-parse-integer/src/lib.rs +++ b/lexical-parse-integer/src/lib.rs @@ -1,5 +1,227 @@ //! Fast lexical string-to-integer conversion routines. //! +//! This contains high-performance methods to parse integers from bytes. +//! Using [`from_lexical`] is analogous to [`parse`][`core-parse`], +//! while enabling parsing from bytes as well as [`str`]. +//! +//! [`from_lexical`]: FromLexical::from_lexical +//! [`core-parse`]: core::str::FromStr +//! +//! # Getting Started +//! +//! To parse a number from bytes, use [`from_lexical`]: +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_parse_integer::{Error, FromLexical}; +//! +//! let value = u64::from_lexical("1234".as_bytes()); +//! assert_eq!(value, Ok(1234)); +//! +//! let value = u64::from_lexical("18446744073709551616".as_bytes()); +//! assert_eq!(value, Err(Error::Overflow(19))); +//! +//! let value = u64::from_lexical("1234 }, {\"Key\", \"Value\"}}".as_bytes()); +//! assert_eq!(value, Err(Error::InvalidDigit(4))); +//! ``` +//! +//! If wishing to incrementally parse a string from bytes, that is, parse as +//! many characters until an invalid digit is found, you can use the partial +//! parsers. This is useful in parsing data where the type is known, such as +//! JSON, but where the end of the number is not yet known. +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_parse_integer::{Error, FromLexical}; +//! +//! let value = u64::from_lexical_partial("1234 }, {\"Key\", \"Value\"}}".as_bytes()); +//! assert_eq!(value, Ok((1234, 4))); +//! +//! let value = u64::from_lexical_partial("18446744073709551616 }, {\"Key\", \"Value\"}}".as_bytes()); +//! assert_eq!(value, Err(Error::Overflow(19))); +//! ``` +//! +//! # Options/Formatting API +//! +//! Each integer parser contains extensive formatting control through +//! [`mod@format`], particularly digit [`separator`] support (that is, +//! integers such as `1_2__3`). For options, we have custom formats +//! optimized for both [`small`] and [`large`] integers. +//! +//! [`small`]: crate::options::SMALL_NUMBERS +//! [`large`]: crate::options::LARGE_NUMBERS +//! [`separator`]: NumberFormat::digit_separator +//! +//! To optimize for smaller integers at the expense of performance of larger +//! ones, you can use [`OptionsBuilder::no_multi_digit`] (defaults to [`true`]). +//! +//! ```rust +//! # use core::{num, str}; +//! use lexical_parse_integer::{options, NumberFormatBuilder, FromLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::new().build_strict(); +//! +//! // a bit faster +//! let value = u64::from_lexical_with_options::(b"12", &options::SMALL_NUMBERS); +//! assert_eq!(value, Ok(12)); +//! +//! // a lot slower +//! let value = u64::from_lexical_with_options::(b"18446744073709551615", &options::SMALL_NUMBERS); +//! assert_eq!(value, Ok(0xffffffffffffffff)); +//! ``` +//! +//! # Features +//! +//! * `format` - Add support for parsing custom integer formats. +//! * `power-of-two` - Add support for parsing power-of-two integer strings. +//! * `radix` - Add support for strings of any radix. +//! * `compact` - Reduce code size at the cost of performance. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. +//! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! +//! A complete description of supported features includes: +//! +//! #### format +//! +//! Add support custom float formatting specifications. This should be used in +//! conjunction with [`Options`] for extensible integer parsing. This allows +//! changing the use of digit separators, requiring or not allowing signs, and +//! more. +//! +//! ##### JSON +//! +//! For example, in JSON, the following integers are valid or invalid: +//! +//! ```text +//! -1 // valid +//! +1 // invalid +//! 1 // valid +//! ``` +//! +//! All of these are valid in our default format (the format of Rust strings), +//! so we must use a custom format to parse JSON strings: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! # use core::str; +//! use lexical_parse_integer::{format, Error, FromLexicalWithOptions, Options}; +//! +//! const OPTIONS: Options = Options::new(); +//! let value = u64::from_lexical_with_options::<{ format::JSON }>("1234".as_bytes(), &OPTIONS); +//! assert_eq!(value, Ok(1234)); +//! +//! let value = u64::from_lexical_with_options::<{ format::JSON }>("+1234".as_bytes(), &OPTIONS); +//! assert_eq!(value, Err(Error::InvalidPositiveSign(0))); +//! # } +//! ``` +//! +//! ##### Custom Format +//! +//! An example of building a custom format to with digit separator support is: +//! +//! ```rust +//! # #[cfg(all(feature = "format", feature = "power-of-two"))] { +//! # use core::{num, str}; +//! use lexical_parse_integer::{NumberFormatBuilder, Options, FromLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::new() +//! // require that a `+` or `-` preceeds the number +//! .required_mantissa_sign(true) +//! // allow internal digit separators, that is, a special character between digits +//! .integer_internal_digit_separator(true) +//! // use `_` as the digit separator +//! .digit_separator(num::NonZeroU8::new(b'_')) +//! // allow an optional `0d` prefix to the number +//! .base_prefix(num::NonZeroU8::new(b'd')) +//! // build the number format, panicking on error +//! .build_strict(); +//! const OPTIONS: Options = Options::new(); +//! +//! let value = u64::from_lexical_with_options::("+12_3_4".as_bytes(), &OPTIONS); +//! assert_eq!(value, Ok(1234)); +//! +//! let value = u64::from_lexical_with_options::("+0d12_3_4".as_bytes(), &OPTIONS); +//! assert_eq!(value, Ok(1234)); +//! # } +//! ``` +//! +//! For a list of all supported fields, see [Parse Integer +//! Fields][NumberFormatBuilder#parse-integer-fields]. +//! +//! Enabling the [`format`](crate#format) API significantly increases compile +//! times, however, it enables a large amount of customization in how integers +//! are parsed. +//! +//! #### power-of-two +//! +//! Enable parsing numbers that are powers of two, that is, `2`, `4`, `8`, `16`, +//! and `32`. +//! +//! ```rust +//! # #[no_std] +//! # #[cfg(feature = "power-of-two")] { +//! # use core::str; +//! use lexical_parse_integer::{FromLexicalWithOptions, NumberFormatBuilder, Options}; +//! +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! const OPTIONS: Options = Options::new(); +//! let value = u64::from_lexical_with_options::("10011010010".as_bytes(), &OPTIONS); +//! assert_eq!(value, Ok(1234)); +//! # } +//! ``` +//! +//! #### radix +//! +//! Enable parsing numbers using all radixes from `2` to `36`. This requires +//! more static storage than [`power-of-two`][crate#power-of-two], and increases +//! compile times, but can be quite useful for esoteric programming languages +//! which use duodecimal integers. +//! +//! ```rust +//! # #[no_std] +//! # #[cfg(feature = "radix")] { +//! # use core::str; +//! use lexical_parse_integer::{FromLexicalWithOptions, NumberFormatBuilder, Options}; +//! +//! const BINARY: u128 = NumberFormatBuilder::from_radix(12); +//! const OPTIONS: Options = Options::new(); +//! let value = u64::from_lexical_with_options::("86A".as_bytes(), &OPTIONS); +//! assert_eq!(value, Ok(1234)); +//! # } +//! ``` +//! +//! #### compact +//! +//! Reduce the generated code size at the cost of performance. This minimizes +//! the number of static tables, inlining, and generics used, drastically +//! reducing the size of the generated binaries. However, this resulting +//! performance of the generated code is much lower. +//! +//! #### std +//! +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. +//! +//! # Higher-Level APIs +//! +//! If you would like an API that supports multiple numeric conversions rather +//! than just writing integers, use [`lexical`] or [`lexical-core`] instead. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! +//! # Algorithm +//! //! The default implementations are highly optimized both for simple //! strings, as well as input with large numbers of digits. In order to //! keep performance optimal for simple strings, we avoid overly branching @@ -18,42 +240,13 @@ //! unnecessary branching and produces smaller binaries, but comes //! at a significant performance penalty for integers with more digits. //! -//! # Features -//! -//! * `std` - Use the standard library. -//! * `power-of-two` - Add support for parsing power-of-two integer strings. -//! * `radix` - Add support for strings of any radix. -//! * `format` - Add support for parsing custom integer formats. -//! * `compact` - Reduce code size at the cost of performance. -//! * `safe` - Ensure only memory-safe indexing is used. -//! -//! `safe` is a no-op, since all parsers are memory-safe by default. -//! -//! # Note -//! -//! Only documented functionality is considered part of the public API: -//! any of the modules, internal functions, or structs may change -//! release-to-release without major or minor version changes. Use -//! internal implementation details at your own risk. -//! -//! lexical-parse-integer mainly exists as an implementation detail for -//! lexical-core, although its API is stable. If you would like to use -//! a high-level API that writes to and parses from `String` and `&str`, -//! respectively, please look at [lexical](https://crates.io/crates/lexical) -//! instead. If you would like an API that supports multiple numeric -//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) -//! instead. -//! -//! # Version Support -//! -//! The minimum, standard, required version is 1.61.0, for const generic -//! support. Older versions of lexical support older Rust versions. -//! //! # Design //! //! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) //! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Benchmarks.md) //! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html // FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below // Clippy reasons were stabilized in 1.81.0. @@ -63,6 +256,8 @@ #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -96,7 +291,7 @@ mod api; // Re-exports pub use lexical_util::error::Error; -pub use lexical_util::format::{self, NumberFormatBuilder}; +pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder}; pub use lexical_util::options::ParseOptions; pub use lexical_util::result::Result; diff --git a/lexical-parse-integer/src/options.rs b/lexical-parse-integer/src/options.rs index fadc01df..94fedf77 100644 --- a/lexical-parse-integer/src/options.rs +++ b/lexical-parse-integer/src/options.rs @@ -1,10 +1,48 @@ //! Configuration options for parsing integers. +//! +//! # Pre-Defined Formats +//! +//! This contains pre-defined options optimized for either the parsing of large +//! or small numbers. +//! - [`SMALL_NUMBERS`][`SMALL_NUMBERS`]: Optimize the parsing of small +//! integers, at a major performance cost to larger values. +//! - [`LARGE_NUMBERS`][`LARGE_NUMBERS`]: Optimize the parsing of large +//! integers, at a slight performance cost to smaller values. +//! +//! # Examples +//! +//! ```rust +//! use lexical_parse_integer::{FromLexicalWithOptions, Options}; +//! use lexical_parse_integer::format::STANDARD; +//! +//! const OPTIONS: Options = Options::builder() +//! .no_multi_digit(true) +//! .build_strict(); +//! +//! let value = "1234"; +//! let result = u64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +//! assert_eq!(result, Ok(1234)); +//! ``` use lexical_util::options::ParseOptions; use lexical_util::result::Result; -use static_assertions::const_assert; -/// Builder for `Options`. +/// Builder for [`Options`]. +/// +/// # Examples +/// +/// ```rust +/// use lexical_parse_integer::{FromLexicalWithOptions, Options}; +/// use lexical_parse_integer::format::STANDARD; +/// +/// const OPTIONS: Options = Options::builder() +/// .no_multi_digit(true) +/// .build_strict(); +/// +/// let value = "1234"; +/// let result = u64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +/// assert_eq!(result, Ok(1234)); +/// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OptionsBuilder { /// Disable multi-digit optimizations. @@ -29,6 +67,17 @@ impl OptionsBuilder { // GETTERS /// Get if we disable the use of multi-digit optimizations. + /// + /// Defaults to [`true`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_integer::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_no_multi_digit(), true); + /// ``` #[inline(always)] pub const fn get_no_multi_digit(&self) -> bool { self.no_multi_digit @@ -37,6 +86,19 @@ impl OptionsBuilder { // SETTERS /// Set if we disable the use of multi-digit optimizations. + /// + /// Defaults to [`true`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_integer::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .no_multi_digit(false) + /// .build_strict(); + /// assert_eq!(OPTIONS.get_no_multi_digit(), false); + /// ``` #[inline(always)] pub const fn no_multi_digit(mut self, no_multi_digit: bool) -> Self { self.no_multi_digit = no_multi_digit; @@ -45,13 +107,23 @@ impl OptionsBuilder { // BUILDERS - /// Check if the builder state is valid. + /// Check if the builder state is valid (always [`true`]). #[inline(always)] pub const fn is_valid(&self) -> bool { true } - /// Build the Options struct with bounds validation. + /// Build the [`Options`] struct without validation. + /// + ///
+ /// + /// This is completely safe, however, misusing this could cause panics at + /// runtime. Always check if [`is_valid`] prior to using the built + /// options. + /// + ///
+ /// + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn build_unchecked(&self) -> Options { Options { @@ -59,7 +131,16 @@ impl OptionsBuilder { } } - /// Build the Options struct. + /// Build the [`Options`] struct. This can never panic. + #[inline(always)] + pub const fn build_strict(&self) -> Options { + match self.build() { + Ok(value) => value, + Err(error) => core::panic!("{}", error.description()), + } + } + + /// Build the [`Options`] struct. Always [`Ok`]. #[inline(always)] pub const fn build(&self) -> Result { Ok(self.build_unchecked()) @@ -73,18 +154,21 @@ impl Default for OptionsBuilder { } } -/// Immutable options to customize writing integers. +/// Options to customize the parsing integers. /// /// # Examples /// /// ```rust -/// use lexical_parse_integer::options::Options; +/// use lexical_parse_integer::{FromLexicalWithOptions, Options}; +/// use lexical_parse_integer::format::STANDARD; /// -/// # pub fn main() { -/// let options = Options::builder() -/// .build() -/// .unwrap(); -/// # } +/// const OPTIONS: Options = Options::builder() +/// .no_multi_digit(true) +/// .build_strict(); +/// +/// let value = "1234"; +/// let result = u64::from_lexical_with_options::(value.as_bytes(), &OPTIONS); +/// assert_eq!(result, Ok(1234)); /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Options { @@ -99,7 +183,7 @@ pub struct Options { } impl Options { - /// Create options with default values. + /// Create [`Options`] with default values. #[must_use] #[inline(always)] pub const fn new() -> Self { @@ -108,13 +192,26 @@ impl Options { // GETTERS - /// Check if the options state is valid. + /// Check if the builder state is valid (always [`true`]). #[inline(always)] pub const fn is_valid(&self) -> bool { self.rebuild().is_valid() } /// Get if we disable the use of multi-digit optimizations. + /// + /// Defaults to [`true`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_parse_integer::options::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .no_multi_digit(true) + /// .build_strict(); + /// assert_eq!(OPTIONS.get_no_multi_digit(), true); + /// ``` #[inline(always)] pub const fn get_no_multi_digit(&self) -> bool { self.no_multi_digit @@ -123,20 +220,28 @@ impl Options { // SETTERS /// Set if we disable the use of multi-digit optimizations. + #[deprecated = "Setters should have a `set_` prefix. Use `set_no_multi_digit` instead. Will be removed in 2.0."] #[inline(always)] pub fn no_multi_digit(&mut self, no_multi_digit: bool) { self.no_multi_digit = no_multi_digit; } + /// Set if we disable the use of multi-digit optimizations. + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] + #[inline(always)] + pub fn set_no_multi_digit(&mut self, no_multi_digit: bool) { + self.no_multi_digit = no_multi_digit; + } + // BUILDERS - /// Get `OptionsBuilder` as a static function. + /// Get [`OptionsBuilder`] as a static function. #[inline(always)] pub const fn builder() -> OptionsBuilder { OptionsBuilder::new() } - /// Create `OptionsBuilder` using existing values. + /// Create [`OptionsBuilder`] using existing values. #[inline(always)] pub const fn rebuild(&self) -> OptionsBuilder { OptionsBuilder { @@ -165,18 +270,15 @@ impl ParseOptions for Options { /// Standard number format. #[rustfmt::skip] pub const STANDARD: Options = Options::new(); -const_assert!(STANDARD.is_valid()); /// Options optimized for small numbers. #[rustfmt::skip] pub const SMALL_NUMBERS: Options = Options::builder() - .no_multi_digit(true) - .build_unchecked(); -const_assert!(SMALL_NUMBERS.is_valid()); + .no_multi_digit(true) + .build_strict(); /// Options optimized for large numbers and long strings. #[rustfmt::skip] pub const LARGE_NUMBERS: Options = Options::builder() - .no_multi_digit(false) - .build_unchecked(); -const_assert!(LARGE_NUMBERS.is_valid()); + .no_multi_digit(false) + .build_strict(); diff --git a/lexical-parse-integer/tests/api_tests.rs b/lexical-parse-integer/tests/api_tests.rs index f87187cf..4c25c8b5 100644 --- a/lexical-parse-integer/tests/api_tests.rs +++ b/lexical-parse-integer/tests/api_tests.rs @@ -147,23 +147,23 @@ fn double_sign_test() { #[test] fn options_test() { - let options = Options::new(); - assert_eq!(Ok(0), i128::from_lexical_with_options::(b"0", &options)); + const OPTIONS: Options = Options::new(); + assert_eq!(Ok(0), i128::from_lexical_with_options::(b"0", &OPTIONS)); } #[test] #[cfg(feature = "power-of-two")] fn i32_binary_test() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = from_radix(2); - assert_eq!(i32::from_lexical_with_options::(b"11", &options), Ok(3)); - assert_eq!(i32::from_lexical_with_options::(b"-11", &options), Ok(-3)); + assert_eq!(i32::from_lexical_with_options::(b"11", &OPTIONS), Ok(3)); + assert_eq!(i32::from_lexical_with_options::(b"-11", &OPTIONS), Ok(-3)); } #[cfg(feature = "radix")] fn radix_to_u32(bytes: &[u8], expected: u32) { - let options = Options::new(); - let result = u32::from_lexical_with_options::<{ FORMAT }>(bytes, &options); + const OPTIONS: Options = Options::new(); + let result = u32::from_lexical_with_options::<{ FORMAT }>(bytes, &OPTIONS); assert_eq!(result, Ok(expected)); } @@ -210,82 +210,82 @@ fn radix_test() { #[test] #[cfg(feature = "format")] fn i32_no_leading_zeros_test() { - let options = Options::new(); - const FORMAT: u128 = NumberFormatBuilder::new().no_integer_leading_zeros(true).build(); - assert!(i32::from_lexical_with_options::(b"1", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"0", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"01", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"10", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"010", &options).is_err()); + const OPTIONS: Options = Options::new(); + const FORMAT: u128 = NumberFormatBuilder::new().no_integer_leading_zeros(true).build_strict(); + assert!(i32::from_lexical_with_options::(b"1", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"0", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"01", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"10", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"010", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_internal_digit_separator_test() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) - .build(); - assert!(i32::from_lexical_with_options::(b"3_1", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); + .build_strict(); + assert!(i32::from_lexical_with_options::(b"3_1", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"_31", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"31_", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_leading_digit_separator_test() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_leading_digit_separator(true) - .build(); + .build_strict(); - assert!(i32::from_lexical_with_options::(b"3_1", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"_31", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); + assert!(i32::from_lexical_with_options::(b"3_1", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"_31", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"31_", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn i32_integer_trailing_digit_separator_test() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_trailing_digit_separator(true) - .build(); + .build_strict(); - assert!(i32::from_lexical_with_options::(b"3_1", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"31_", &options).is_ok()); + assert!(i32::from_lexical_with_options::(b"3_1", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"_31", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"31_", &OPTIONS).is_ok()); } #[test] #[cfg(feature = "format")] fn i32_integer_consecutive_digit_separator_test() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(std::num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); + .build_strict(); - assert!(i32::from_lexical_with_options::(b"3_1", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"3__1", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"_31", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"31_", &options).is_err()); + assert!(i32::from_lexical_with_options::(b"3_1", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"3__1", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"_31", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"31_", &OPTIONS).is_err()); } #[test] #[cfg(feature = "format")] fn i32_json_no_leading_zero() { - let options = Options::new(); + const OPTIONS: Options = Options::new(); use lexical_util::format::JSON; - assert!(i32::from_lexical_with_options::<{ JSON }>(b"12", &options).is_ok()); - assert!(i32::from_lexical_with_options::<{ JSON }>(b"-12", &options).is_ok()); - assert!(i32::from_lexical_with_options::<{ JSON }>(b"012", &options).is_err()); - assert!(i32::from_lexical_with_options::<{ JSON }>(b"-012", &options).is_err()); + assert!(i32::from_lexical_with_options::<{ JSON }>(b"12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::<{ JSON }>(b"-12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::<{ JSON }>(b"012", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::<{ JSON }>(b"-012", &OPTIONS).is_err()); } #[test] @@ -293,23 +293,24 @@ fn i32_json_no_leading_zero() { fn base_prefix_test() { use core::num; - const FORMAT: u128 = NumberFormatBuilder::new().base_prefix(num::NonZeroU8::new(b'x')).build(); - let options = Options::new(); - - assert!(i32::from_lexical_with_options::(b"0x", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-0x", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-0x1", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"0x12", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"12", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-0x12", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"0x-12", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"012", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-0x012", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-0x012h", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-0x012h ", &options).is_err()); - - assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &options).is_ok()); - assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &options).is_ok()); + const FORMAT: u128 = + NumberFormatBuilder::new().base_prefix(num::NonZeroU8::new(b'x')).build_strict(); + const OPTIONS: Options = Options::new(); + + assert!(i32::from_lexical_with_options::(b"0x", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-0x", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-0x1", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"0x12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-0x12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"0x-12", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"012", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-0x012", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-0x012h", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-0x012h ", &OPTIONS).is_err()); + + assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &OPTIONS).is_ok()); } #[test] @@ -317,24 +318,25 @@ fn base_prefix_test() { fn base_suffix_test() { use core::num; - const FORMAT: u128 = NumberFormatBuilder::new().base_suffix(num::NonZeroU8::new(b'h')).build(); - let options = Options::new(); - - assert!(i32::from_lexical_with_options::(b"h", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-h", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-1h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"12h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"12", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-12h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"0x-12", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"0x12", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"012h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-012", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"-0x012h", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"-0x012h ", &options).is_err()); - - assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &options).is_ok()); - assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &options).is_ok()); + const FORMAT: u128 = + NumberFormatBuilder::new().base_suffix(num::NonZeroU8::new(b'h')).build_strict(); + const OPTIONS: Options = Options::new(); + + assert!(i32::from_lexical_with_options::(b"h", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-h", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-1h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"12h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"12", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-12h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"0x-12", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"0x12", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"012h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-012", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"-0x012h", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"-0x012h ", &OPTIONS).is_err()); + + assert!(i32::from_lexical_partial_with_options::(b"-0x012h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_partial_with_options::(b"-0x012h ", &OPTIONS).is_ok()); } #[test] @@ -345,13 +347,13 @@ fn base_prefix_and_suffix_test() { const FORMAT: u128 = NumberFormatBuilder::new() .base_prefix(num::NonZeroU8::new(b'x')) .base_suffix(num::NonZeroU8::new(b'h')) - .build(); - let options = Options::new(); - assert!(i32::from_lexical_with_options::(b"+3h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"+0x3", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"+0x3h", &options).is_ok()); - assert!(i32::from_lexical_with_options::(b"+0x3h ", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"+0xh", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"+h", &options).is_err()); - assert!(i32::from_lexical_with_options::(b"+0x", &options).is_err()); + .build_strict(); + const OPTIONS: Options = Options::new(); + assert!(i32::from_lexical_with_options::(b"+3h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"+0x3", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"+0x3h", &OPTIONS).is_ok()); + assert!(i32::from_lexical_with_options::(b"+0x3h ", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"+0xh", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"+h", &OPTIONS).is_err()); + assert!(i32::from_lexical_with_options::(b"+0x", &OPTIONS).is_err()); } diff --git a/lexical-parse-integer/tests/issue_96_tests.rs b/lexical-parse-integer/tests/issue_96_tests.rs index d6c994b0..85c08442 100644 --- a/lexical-parse-integer/tests/issue_96_tests.rs +++ b/lexical-parse-integer/tests/issue_96_tests.rs @@ -12,360 +12,360 @@ use lexical_parse_integer::{ #[test] fn issue_96_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const NO_CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); const CONSECUTIVE: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); const NO_LEADING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(false) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); + .build_strict(); let result = i64::from_lexical(b"_-1234"); assert_eq!(result, Err(Error::InvalidDigit(0))); // NOTE: We need to make sure we're not skipping digit separators before the // sign, which is never allowed. - let result = u64::from_lexical_with_options::(b"_-1234", &opts); + let result = u64::from_lexical_with_options::(b"_-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(1))); - let result = i64::from_lexical_with_options::(b"_-1234", &opts); + let result = i64::from_lexical_with_options::(b"_-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(1))); - let result = i64::from_lexical_with_options::(b"^-1234", &opts); + let result = i64::from_lexical_with_options::(b"^-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); // NOTE: This uis correct, since it's "trailing" - let result = i64::from_lexical_with_options::(b"_-1234", &opts); + let result = i64::from_lexical_with_options::(b"_-1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(1))); - let result = i64::from_lexical_with_options::(b"_1234", &opts); + let result = i64::from_lexical_with_options::(b"_1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = i64::from_lexical_with_options::(b"X1234", &opts); + let result = i64::from_lexical_with_options::(b"X1234", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = i64::from_lexical_with_options::(b"__1__234__", &opts); + let result = i64::from_lexical_with_options::(b"__1__234__", &OPTS); assert_eq!(result, Err(Error::InvalidDigit(0))); - let result = i64::from_lexical_with_options::(b"__1__234__", &opts); + let result = i64::from_lexical_with_options::(b"__1__234__", &OPTS); assert_eq!(result, Ok(1234)); } #[test] fn issue_96_i_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .consecutive_digit_separator(false) .required_digits(true) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"_1_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11, 3))); - let result = i64::from_lexical_partial_with_options::(b"1_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_23", &OPTS); assert_eq!(result, Ok((1123, 6))); - let result = i64::from_lexical_partial_with_options::(b"1_1__23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1__23", &OPTS); assert_eq!(result, Ok((11, 3))); - let result = i64::from_lexical_partial_with_options::(b"1_1_23_", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_23_", &OPTS); assert_eq!(result, Ok((1123, 6))); - let result = i64::from_lexical_partial_with_options::(b"1_1_23.", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_23.", &OPTS); assert_eq!(result, Ok((1123, 6))); } #[test] fn issue_96_l_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1, 2))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1, 3))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); } #[test] fn issue_96_t_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123, 5))); - let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_il_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .leading_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((123, 5))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((123, 6))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11, 3))); - let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((11, 3))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123, 4))); - let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_it_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((11, 3))); - let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((11, 4))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123, 5))); - let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_lt_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Err(Error::Empty(0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Err(Error::Empty(2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1, 2))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1, 3))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"_11_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_11_", &OPTS); assert_eq!(result, Ok((11, 4))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Err(Error::Empty(1))); - let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123, 5))); - let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123, 4))); } #[test] fn issue_96_no_required_test() { - let opts = Options::new(); + const OPTS: Options = Options::new(); const FMT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(false) .required_digits(false) - .build(); + .build_strict(); - let result = i64::from_lexical_partial_with_options::(b"", &opts); + let result = i64::from_lexical_partial_with_options::(b"", &OPTS); assert_eq!(result, Ok((0, 0))); - let result = i64::from_lexical_partial_with_options::(b"_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_", &OPTS); assert_eq!(result, Ok((0, 1))); - let result = i64::from_lexical_partial_with_options::(b"+_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_", &OPTS); assert_eq!(result, Ok((0, 2))); - let result = i64::from_lexical_partial_with_options::(b"_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_1_23", &OPTS); assert_eq!(result, Ok((1, 2))); - let result = i64::from_lexical_partial_with_options::(b"+_1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"+_1_23", &OPTS); assert_eq!(result, Ok((1, 3))); - let result = i64::from_lexical_partial_with_options::(b"1__1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"1__1_23", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"1_1_", &opts); + let result = i64::from_lexical_partial_with_options::(b"1_1_", &OPTS); assert_eq!(result, Ok((1, 1))); - let result = i64::from_lexical_partial_with_options::(b"_11_", &opts); + let result = i64::from_lexical_partial_with_options::(b"_11_", &OPTS); assert_eq!(result, Ok((11, 4))); - let result = i64::from_lexical_partial_with_options::(b"_+1_23", &opts); + let result = i64::from_lexical_partial_with_options::(b"_+1_23", &OPTS); assert_eq!(result, Ok((0, 1))); - let result = i64::from_lexical_partial_with_options::(b"+123_", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123_", &OPTS); assert_eq!(result, Ok((123, 5))); - let result = i64::from_lexical_partial_with_options::(b"+123__", &opts); + let result = i64::from_lexical_partial_with_options::(b"+123__", &OPTS); assert_eq!(result, Ok((123, 4))); } diff --git a/lexical-parse-integer/tests/issue_98_tests.rs b/lexical-parse-integer/tests/issue_98_tests.rs index b41c1a93..bd585e9f 100644 --- a/lexical-parse-integer/tests/issue_98_tests.rs +++ b/lexical-parse-integer/tests/issue_98_tests.rs @@ -13,7 +13,7 @@ fn issue_98_test() { .no_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(false) - .build(); + .build_strict(); let result = i64::from_lexical_with_options::(b"1.1.0", &Options::new()); assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); diff --git a/lexical-parse-integer/tests/partial_tests.rs b/lexical-parse-integer/tests/partial_tests.rs index 32d1aa36..6c72e1ac 100644 --- a/lexical-parse-integer/tests/partial_tests.rs +++ b/lexical-parse-integer/tests/partial_tests.rs @@ -13,8 +13,8 @@ fn u8_decimal_test() { assert_eq!(Err(Error::InvalidDigit(0)), u8::from_lexical(b"-1")); assert_eq!(Ok((1, 1)), u8::from_lexical_partial(b"1a")); - let options = Options::default(); - assert_eq!(Ok((0, 1)), u8::from_lexical_partial_with_options::<{ STANDARD }>(b"0", &options)); + const OPTIONS: Options = Options::new(); + assert_eq!(Ok((0, 1)), u8::from_lexical_partial_with_options::<{ STANDARD }>(b"0", &OPTIONS)); } #[test] @@ -22,13 +22,13 @@ fn u8_decimal_test() { fn u8_decimal_format_test() { // Test an invalid format. const FORMAT: u128 = NumberFormatBuilder::from_radix(1); - let options = Options::default(); + const OPTIONS: Options = Options::new(); assert_eq!( Err(Error::InvalidMantissaRadix), - u8::from_lexical_with_options::(b"0", &options) + u8::from_lexical_with_options::(b"0", &OPTIONS) ); assert_eq!( Err(Error::InvalidMantissaRadix), - u8::from_lexical_partial_with_options::(b"0", &options) + u8::from_lexical_partial_with_options::(b"0", &OPTIONS) ); } diff --git a/lexical-util/Cargo.toml b/lexical-util/Cargo.toml index 4553fcf0..30aa44ea 100644 --- a/lexical-util/Cargo.toml +++ b/lexical-util/Cargo.toml @@ -19,7 +19,6 @@ exclude = [ ] [dependencies] -static_assertions = "1" float16 = { version = "0.1.0", optional = true } # FEATURES @@ -75,3 +74,4 @@ f128 = ["parse-floats", "write-floats"] [package.metadata.docs.rs] features = ["radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-util/docs/Safety.md b/lexical-util/docs/Safety.md new file mode 100644 index 00000000..c072af81 --- /dev/null +++ b/lexical-util/docs/Safety.md @@ -0,0 +1,53 @@ +# Safety + +Due to the use of unsafe indexing, the guarantees as well the logic on how to safely implement the API is documented here. + +The only major sources of unsafe code are wrapped in the [`iterator.rs`], [`skip.rs`], and [`noskip.rs`]. These are fully encapsulated into standalone traits to clearly define safety invariants and localize any unsafety to 1 or 2 lines of code. + +The core, unsafe trait is [`DigitsIter`] and [`Iter`], both which expect to be backed by a contiguous block of memory (a slice) but may skip bytes internally. To guarantee safety, for non-skip iterators you must implement [`DigitsIter::is_consumed`][`is_consumed`] correctly. + +This must correctly determine if there are any elements left in the iterator. If the buffer is contiguous, this can just be `index == self.len()`, but for a non-contiguous iterator it must skip any digits to advance to the element next to be returned or the iterator itself will be unsafe. **ALL** other safety invariants depend on this being implemented correctly. + +To see if the cursor is at the end of the buffer, use [`is_buffer_empty`]. + +Any iterators must be peekable: you must be able to read and return the next value without advancing the iterator past that point. For iterators that skip bytes, this means advancing to the next element to be returned and returning that value. + +For examples of how to safely implement skip iterators, you can do something like: + +```rust +impl<_> DigitsIter<_> for MyIter { + fn peek(&mut self) -> Option { + loop { + let value = self.bytes.get(self.index)?; + if value != &b'.' { + return value; + } + self.index += 1; + } + } +} +``` + +Then, [`next`] will be implemented in terms of [`peek`], incrementing the position in the cursor just after the value. The next iteration of peek will step to the correct byte to return. + +```rust,ignore +impl<_> Iterator for MyIter { + type Item = &'a u8; + + fn next(&mut self) -> Option { + let value = self.peek()?; + self.index += 1; + Some(value) + } +} +``` + +[`is_buffer_empty`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#76 +[`is_consumed`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L276 +[`peek`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L284 +[`next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next +[`iterator.rs`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs +[`skip.rs`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/skip.rs +[`noskip.rs`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/noskip.rs +[`DigitsIter`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L250 +[`Iter`]: https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L39 diff --git a/lexical-util/src/algorithm.rs b/lexical-util/src/algorithm.rs index 2f3051b9..06fe6396 100644 --- a/lexical-util/src/algorithm.rs +++ b/lexical-util/src/algorithm.rs @@ -8,6 +8,7 @@ use crate::num::Integer; /// performance isn't the highest consideration here. #[inline(always)] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn copy_to_dst>(dst: &mut [T], src: Bytes) -> usize { let src = src.as_ref(); dst[..src.len()].copy_from_slice(src); @@ -18,6 +19,7 @@ pub fn copy_to_dst>(dst: &mut [T], src: Bytes) -> usi /// Count the number of trailing characters equal to a given value. #[inline(always)] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn rtrim_char_count(slc: &[u8], c: u8) -> usize { slc.iter().rev().take_while(|&&si| si == c).count() } @@ -25,6 +27,7 @@ pub fn rtrim_char_count(slc: &[u8], c: u8) -> usize { /// Count the number of leading characters equal to a given value. #[inline(always)] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn ltrim_char_count(slc: &[u8], c: u8) -> usize { slc.iter().take_while(|&&si| si == c).count() } @@ -32,11 +35,9 @@ pub fn ltrim_char_count(slc: &[u8], c: u8) -> usize { /// Check to see if parsing the float cannot possible overflow. /// /// This allows major optimizations for those types, since we can skip checked -/// arithmetic. +/// arithmetic. Adapted from the rust [corelib][`core`]. /// -/// Adapted from the rust [corelib](core). -/// -/// core: +/// [`core`]: #[inline(always)] pub fn cannot_overflow(length: usize, radix: u32) -> bool { length <= T::overflow_digits(radix) diff --git a/lexical-util/src/api.rs b/lexical-util/src/api.rs index 771285bf..88df0e34 100644 --- a/lexical-util/src/api.rs +++ b/lexical-util/src/api.rs @@ -7,46 +7,100 @@ // FROM LEXICAL -/// Define `FromLexical` trait. +/// Define the [`FromLexical`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `len`: The length of the string form of `value`. +/// +/// # Examples +/// +/// ```rust,ignore +/// from_lexical!("lexical_core", 1234, u64, 4); +/// ``` +/// +/// [`FromLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexical.html #[macro_export] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] macro_rules! from_lexical { - () => { + ($name:literal, $value:literal, $t:ty, $len:literal $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be parsed from bytes. + $(#[$attr])? pub trait FromLexical: lexical_util::num::Number { /// Checked parser for a string-to-number conversion. /// /// This method parses the entire string, returning an error if - /// any invalid digits are found during parsing. Returns a `Result` + /// any invalid digits are found during parsing. Returns a [`Result`] /// containing either the parsed value, or an error containing /// any errors that occurred during parsing. /// /// * `bytes` - Slice containing a numeric string. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::FromLexical;")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical(value.as_bytes());")] + #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")] + /// ``` fn from_lexical(bytes: &[u8]) -> lexical_util::result::Result; /// Checked parser for a string-to-number conversion. /// /// This method parses until an invalid digit is found (or the end /// of the string), returning the number of processed digits - /// and the parsed value until that point. Returns a `Result` + /// and the parsed value until that point. Returns a [`Result`] /// containing either the parsed value and the number of processed /// digits, or an error containing any errors that occurred during /// parsing. /// /// * `bytes` - Slice containing a numeric string. + /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::FromLexical;")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_partial(value.as_bytes());")] + #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")] + /// ``` fn from_lexical_partial(bytes: &[u8]) -> lexical_util::result::Result<(Self, usize)>; } }; } -/// Define `FromLexicalWithOptions` trait. +/// Define the [`FromLexicalWithOptions`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `len`: The length of the string form of `value`. +/// * `ops_t`: The options type. +/// +/// # Examples +/// +/// ```rust,ignore +/// from_lexical_with_options!("lexical_core", 1234, u64, 4, ParseIntegerOptions); +/// ``` +/// +/// [`FromLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexicalWithOptions.html #[macro_export] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] macro_rules! from_lexical_with_options { - () => { + ($name:literal, $value:literal, $t:ty, $len:literal, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be parsed from bytes with custom options. /// - /// The `Options` type specifies the configurable options to provide. + /// The [`Options`][Self::Options] type specifies the configurable + /// options to provide. + $(#[$attr])? pub trait FromLexicalWithOptions: lexical_util::num::Number { /// Custom formatting options for parsing a number. type Options: lexical_util::options::ParseOptions; @@ -57,7 +111,7 @@ macro_rules! from_lexical_with_options { /// any invalid digits are found during parsing. The parsing /// is dictated by the options, which specifies special /// float strings, required float components, digit separators, - /// exponent characters, and more. Returns a `Result` containing + /// exponent characters, and more. Returns a [`Result`] containing /// either the parsed value, or an error containing any errors /// that occurred during parsing. /// @@ -70,6 +124,19 @@ macro_rules! from_lexical_with_options { /// the appropriate format error. If you are unsure which format /// to use, use [`STANDARD`]. /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!("let parsed = ", stringify!($t), "::from_lexical_with_options::(value.as_bytes(), &OPTIONS);")] + #[doc = concat!("assert_eq!(parsed, Ok(", stringify!($value), "));")] + /// ``` + /// /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder /// [`STANDARD`]: lexical_util::format::STANDARD fn from_lexical_with_options( @@ -81,7 +148,7 @@ macro_rules! from_lexical_with_options { /// /// This method parses until an invalid digit is found (or the end /// of the string), returning the number of processed digits - /// and the parsed value until that point. Returns a `Result` + /// and the parsed value until that point. Returns a [`Result`] /// containing either the parsed value and the number of /// processed digits, or an error containing any errors that /// occurred during parsing. @@ -95,6 +162,24 @@ macro_rules! from_lexical_with_options { /// the appropriate format error. If you are unsure which format /// to use, use [`STANDARD`]. /// + /// # Examples + /// + /// ```rust + #[doc = concat!("# assert_eq!(", stringify!($len), ", \"", stringify!($value), "\".len());")] + #[doc = concat!("use ", $name, "::{format, FromLexicalWithOptions, ", stringify!($ops_t), "};")] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + /// + #[doc = concat!("let value = \"", stringify!($value), "\";")] + #[doc = concat!( + "let parsed = ", + stringify!($t), + "::from_lexical_partial_with_options::(value.as_bytes(), &OPTIONS);" + )] + #[doc = concat!("assert_eq!(parsed, Ok((", stringify!($value), ", ", stringify!($len), ")));")] + /// ``` + /// /// [`NumberFormatBuilder`]: lexical_util::format::NumberFormatBuilder /// [`STANDARD`]: lexical_util::format::STANDARD fn from_lexical_partial_with_options( @@ -107,31 +192,63 @@ macro_rules! from_lexical_with_options { // TO LEXICAL -/// Define `ToLexical` trait. +/// Define the [`ToLexical`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// +/// # Examples +/// +/// ```rust,ignore +/// to_lexical!("lexical_core", 1234, u64); +/// ``` +/// +/// [`ToLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexical.html #[macro_export] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] macro_rules! to_lexical { - () => { + ($name:literal, $value:literal, $t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be serialized to bytes. /// /// To determine the number of bytes required to serialize a value to /// string, check the associated constants from a required trait: - /// - [`FORMATTED_SIZE`] - /// - [`FORMATTED_SIZE_DECIMAL`] + /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any + /// radix, that is, `2` to `36`. + /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base + /// 10) numbers. /// - /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE - /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL + $(#[$attr])? pub trait ToLexical: lexical_util::constants::FormattedSize + lexical_util::num::Number { /// Serializer for a number-to-string conversion. /// /// Returns a subslice of the input buffer containing the written bytes, - /// starting from the same address in memory as the input slice. + /// starting from the same address in memory as the input slice. That + /// is, the `bytes` provided to the function and the returned buffer + /// reference the same buffer, just with the number of elements truncated + /// to the written digits. /// /// * `value` - Number to serialize. /// * `bytes` - Buffer to write number to. /// + /// # Examples + /// + /// ```rust + /// use core::str; + /// + #[doc = concat!("use ", $name, "::{format, FormattedSize, ToLexical};")] + /// + #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")] + #[doc = concat!("let mut buffer = [0u8; ", stringify!($t), "::FORMATTED_SIZE_DECIMAL];")] + /// let digits = value.to_lexical(&mut buffer); + #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")] + /// ``` + /// /// # Panics /// /// Panics if the buffer is not of sufficient size. The caller @@ -145,23 +262,40 @@ macro_rules! to_lexical { }; } -/// Define `ToLexicalWithOptions` trait. +/// Define the [`ToLexicalWithOptions`] trait. +/// +/// * `name`: The name of the crate calling the function. +/// * `value`: A numerical value to use for the example. +/// * `t`: The type of the number for the example. +/// * `ops_t`: The options type. +/// +/// # Examples +/// +/// ```rust,ignore +/// to_lexical_with_options!("lexical_core", 1234, u64, WriteIntegerOptions); +/// ``` +/// +/// [`ToLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexicalWithOptions.html #[macro_export] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] macro_rules! to_lexical_with_options { - () => { + ($name:literal, $value:literal, $t:ty, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be serialized to bytes with custom /// options. /// /// To determine the number of bytes required to serialize a value to /// string, check the associated constants from a required trait: - /// - [`FORMATTED_SIZE`] - /// - [`FORMATTED_SIZE_DECIMAL`] + /// - [`FORMATTED_SIZE`]: The number of bytes required for any number for any + /// radix, that is, `2` to `36`. + /// - [`FORMATTED_SIZE_DECIMAL`]: The number of bytes required for decimal (base + /// 10) numbers. /// - /// The `Options` type specifies the configurable options to provide. + /// The [`Options`][Self::Options] type specifies the configurable options to provide. /// - /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE - /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`FORMATTED_SIZE_DECIMAL`]: crate::FormattedSize::FORMATTED_SIZE_DECIMAL + $(#[$attr])? pub trait ToLexicalWithOptions: lexical_util::constants::FormattedSize + lexical_util::num::Number { @@ -171,26 +305,63 @@ macro_rules! to_lexical_with_options { /// Serializer for a number-to-string conversion. /// /// Returns a subslice of the input buffer containing the written bytes, - /// starting from the same address in memory as the input slice. + /// starting from the same address in memory as the input slice. That + /// is, the `bytes` provided to the function and the returned buffer + /// reference the same buffer, just with the number of elements truncated + /// to the written digits. /// /// * `FORMAT` - Flags and characters designating the number grammar. /// * `value` - Number to serialize. /// * `bytes` - Buffer to write number to. /// * `options` - Options for number formatting. /// + /// `FORMAT` should be built using [`NumberFormatBuilder`] and includes + /// options such as the numerical radix for writing the value to string. + /// `options` specificies extra, additional configurations such as + /// special values like `NaN` or `+Infinity` for how to serialize + /// the number. + /// + /// [`NumberFormatBuilder`]: crate::NumberFormatBuilder + /// + /// # Examples + /// + /// ```rust + /// use core::str; + /// + #[doc = concat!( + "use ", + $name, + "::{format, FormattedSize, ", + stringify!($ops_t), + ", ToLexicalWithOptions};" + )] + /// + /// const FORMAT: u128 = format::STANDARD; + #[doc = concat!("const OPTIONS: ", stringify!($ops_t), " = ", stringify!($ops_t), "::new();")] + #[doc = concat!( + "const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<", + stringify!($t), + ", FORMAT>();" + )] + /// + #[doc = concat!("let value: ", stringify!($t), " = ", stringify!($value), ";")] + /// let mut buffer = [0u8; BUFFER_SIZE]; + /// let digits = value.to_lexical_with_options::(&mut buffer, &OPTIONS); + #[doc = concat!("assert_eq!(str::from_utf8(digits), Ok(\"", stringify!($value), "\"));")] + /// ``` + /// /// # Panics /// /// Panics if the buffer is not of sufficient size. The caller /// must provide a slice of sufficient size. In order to ensure /// the function will not panic, ensure the buffer has at least - /// [`FORMATTED_SIZE`] elements. If you are changing the - /// number significant digits written, the exponent break points, - /// or disabling scientific notation, you will need a larger buffer - /// than the one provided. An upper limit on the buffer size can - /// then be determined using [`WriteOptions::buffer_size`]. If you - /// are not using `min_significant_digits`, 1200 bytes is always - /// enough to hold the the output for a custom radix, and `400` - /// is always enough for decimal strings. + /// [`Options::buffer_size_const`] elements. This is required + /// only when changing the number of significant digits, the + /// exponent break point, or disabling scientific notation. + /// + /// If you are not using [`min_significant_digits`] (floats only), + /// 1200 bytes is always enough to hold the the output for a custom + /// radix, and `400` is always enough for decimal strings. /// /// **Floats Only** /// @@ -209,11 +380,16 @@ macro_rules! to_lexical_with_options { /// - `32, 2` /// - `16, 4` /// - /// Panics as well if the NaN or Inf string provided to the writer - /// is disabled, but the value provided is NaN or Inf, respectively. + /// Panics as well if `the` NaN or `Inf` string provided to the writer + /// is disabled, but the value provided is `NaN` or `Inf`, respectively. /// - /// [`WriteOptions::buffer_size`]: lexical_util::options::WriteOptions::buffer_size - /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE + #[doc = concat!( + "[`Options::buffer_size_const`]: crate::", + stringify!($ops_t), + "::buffer_size_const" + )] + /// [`FORMATTED_SIZE`]: crate::FormattedSize::FORMATTED_SIZE + /// [`min_significant_digits`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.min_significant_digits fn to_lexical_with_options<'a, const FORMAT: u128>( self, bytes: &'a mut [u8], diff --git a/lexical-util/src/ascii.rs b/lexical-util/src/ascii.rs index a6540edf..ec2f3934 100644 --- a/lexical-util/src/ascii.rs +++ b/lexical-util/src/ascii.rs @@ -1,6 +1,7 @@ //! Utilities for working with ASCII characters. /// Determine if a character is a valid ASCII character for float grammar. +#[inline(always)] pub const fn is_valid_ascii(c: u8) -> bool { // Below 0x20 is mostly control characters, with no representation. // 0x7F is a control character, DEL, so don't include it. @@ -14,7 +15,7 @@ pub const fn is_valid_ascii(c: u8) -> bool { } /// Determine if a slice is all valid ASCII characters for float grammar. -/// Modified to be used in a const fn, since for loops and iter don't work. +#[inline(always)] pub const fn is_valid_ascii_slice(slc: &[u8]) -> bool { let mut index = 0; while index < slc.len() { @@ -27,12 +28,13 @@ pub const fn is_valid_ascii_slice(slc: &[u8]) -> bool { } /// Determine if a character is a valid ASCII letter. +#[inline(always)] pub const fn is_valid_letter(c: u8) -> bool { (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) } /// Determine if a slice is all valid ASCII letters. -/// Modified to be used in a const fn, since for loops and iter don't work. +#[inline(always)] pub const fn is_valid_letter_slice(slc: &[u8]) -> bool { let mut index = 0; while index < slc.len() { diff --git a/lexical-util/src/assert.rs b/lexical-util/src/assert.rs index 5ea4d626..9e01c910 100644 --- a/lexical-util/src/assert.rs +++ b/lexical-util/src/assert.rs @@ -1,5 +1,7 @@ //! Debugging assertions to check a radix is valid. +#![doc(hidden)] + #[cfg(feature = "write")] use crate::constants::FormattedSize; diff --git a/lexical-util/src/constants.rs b/lexical-util/src/constants.rs index 3750a2aa..149c52c8 100644 --- a/lexical-util/src/constants.rs +++ b/lexical-util/src/constants.rs @@ -1,6 +1,8 @@ //! Pre-defined constants for numeric types. +#![doc(hidden)] #![cfg(feature = "write")] +#![cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] #[cfg(feature = "f16")] use crate::bf16::bf16; @@ -10,6 +12,10 @@ use crate::f16::f16; /// The size, in bytes, of formatted values. pub trait FormattedSize { /// Maximum number of bytes required to serialize a number to string. + /// If [`power-of-two`] or [`radix`] is not enabled, this is the same as + /// [`FORMATTED_SIZE_DECIMAL`][`Self::FORMATTED_SIZE_DECIMAL`]. + /// + ///
/// /// Note that this value may be insufficient if digit precision control, /// exponent break points, or disabling exponent notation is used. If @@ -18,13 +24,21 @@ pub trait FormattedSize { /// buffer than the one provided. An upper limit on the buffer size can /// then be determined using [`WriteOptions::buffer_size`]. /// + /// Using an insufficiently large buffer will lead to the code panicking. + /// + ///
+ /// /// [`WriteOptions::buffer_size`]: crate::options::WriteOptions::buffer_size /// [`lexical_write_float`]: https://github.com/Alexhuszagh/rust-lexical/tree/main/lexical-write-float + /// [`power-of-two`]: crate#features + /// [`radix`]: crate#features const FORMATTED_SIZE: usize; /// Maximum number of bytes required to serialize a number to a decimal /// string. /// + ///
+ /// /// Note that this value may be insufficient if digit precision control, /// exponent break points, or disabling exponent notation is used. If /// you are changing the number significant digits written, the exponent @@ -32,6 +46,10 @@ pub trait FormattedSize { /// buffer than the one provided. An upper limit on the buffer size can /// then be determined using [`WriteOptions::buffer_size`]. /// + /// Using an insufficiently large buffer will lead to the code panicking. + /// + ///
+ /// /// [`WriteOptions::buffer_size`]: crate::options::WriteOptions::buffer_size /// [`lexical_write_float`]: https://github.com/Alexhuszagh/rust-lexical/tree/main/lexical-write-float const FORMATTED_SIZE_DECIMAL: usize; @@ -93,7 +111,8 @@ formatted_size_impl! { isize 20 128 ; } #[cfg(target_pointer_width = "64")] formatted_size_impl! { usize 20 128 ; } -/// Maximum number of bytes required to serialize any number to string. +/// Maximum number of bytes required to serialize any number with default +/// options to string. /// /// Note that this value may be insufficient if digit precision control, /// exponent break points, or disabling exponent notation is used. diff --git a/lexical-util/src/digit.rs b/lexical-util/src/digit.rs index 843becf7..40a8380a 100644 --- a/lexical-util/src/digit.rs +++ b/lexical-util/src/digit.rs @@ -55,6 +55,14 @@ pub const fn char_is_digit_const(c: u8, radix: u32) -> bool { /// match-based fallback algorithm. #[inline(always)] #[cfg(any(feature = "write", feature = "floats"))] +#[cfg_attr( + docsrs, + doc(cfg(any( + feature = "parse-floats", + feature = "write-floats", + feature = "write-integers", + ))) +)] pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 { if radix <= 10 || digit < 10 { // Can short-circuit if we know the radix is small at compile time. @@ -74,6 +82,7 @@ pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 { /// Convert a character to a digit. #[inline(always)] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub const fn char_to_digit(c: u8, radix: u32) -> Option { // Fallback, still decently fast. let digit = match c { @@ -92,6 +101,7 @@ pub const fn char_to_digit(c: u8, radix: u32) -> Option { /// Determine if a character is a digit. #[inline(always)] #[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] pub const fn char_is_digit(c: u8, radix: u32) -> bool { char_to_digit(c, radix).is_some() } @@ -104,6 +114,7 @@ pub const fn char_is_digit(c: u8, radix: u32) -> bool { /// Panics if `digit >= 36`. #[inline(always)] #[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] pub fn digit_to_char(digit: u32) -> u8 { const TABLE: [u8; 36] = [ b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', diff --git a/lexical-util/src/div128.rs b/lexical-util/src/div128.rs index 008d856f..c3569ecd 100644 --- a/lexical-util/src/div128.rs +++ b/lexical-util/src/div128.rs @@ -42,7 +42,13 @@ #![cfg(not(feature = "compact"))] #![cfg(feature = "write")] - +#![cfg_attr( + docsrs, + doc(cfg(all( + any(feature = "write-floats", feature = "write-integers"), + not(feature = "compact") + ))) +)] use crate::assert::debug_assert_radix; use crate::mul::mulhi; diff --git a/lexical-util/src/error.rs b/lexical-util/src/error.rs index 6e6b1227..53c2cad2 100644 --- a/lexical-util/src/error.rs +++ b/lexical-util/src/error.rs @@ -3,12 +3,12 @@ //! The error type is C-compatible, simplifying use external language //! bindings. -use core::{fmt, mem}; +#![doc(hidden)] + +use core::fmt; #[cfg(feature = "std")] use std::error; -use static_assertions::const_assert; - /// Error code during parsing, indicating failure type. #[non_exhaustive] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] @@ -120,9 +120,6 @@ pub enum Error { Success, } -// Ensure we don't have extra padding on the structure. -const_assert!(mem::size_of::() <= 2 * mem::size_of::()); - macro_rules! is_error_type { ($name:ident, $type:ident$($t:tt)*) => ( /// const fn check to see if an error is of a specific type. @@ -138,7 +135,71 @@ macro_rules! is_error_type { } impl Error { + /// Get a description of the error in a const, panic friendly way. + #[inline] + pub const fn description(&self) -> &'static str { + match self { + // PARSE ERRORS + Self::Overflow(_) => "'numeric overflow occurred'", + Self::Underflow(_) => "'numeric underflow occurred'", + Self::InvalidDigit(_) => "'invalid digit found'", + Self::Empty(_) => "'the string to parse was empty'", + Self::EmptyMantissa(_) => "'no significant digits found'", + Self::EmptyExponent(_) => "'exponent notation found without an exponent'", + Self::EmptyInteger(_) => "'invalid float with no integer digits'", + Self::EmptyFraction(_) => "'invalid float with no fraction digits'", + Self::InvalidPositiveMantissaSign(_) => "'invalid `+` sign before significant digits'", + Self::MissingMantissaSign(_) => "'missing required `+/-` sign for significant digits'", + Self::InvalidExponent(_) => "'exponent found but not allowed'", + Self::InvalidPositiveExponentSign(_) => "'invalid `+` sign in exponent'", + Self::MissingExponentSign(_) => "'missing required `+/-` sign for exponent'", + Self::ExponentWithoutFraction(_) => "'invalid float containing exponent without fraction'", + Self::InvalidLeadingZeros(_) => "'invalid number with leading zeros before digits'", + Self::MissingExponent(_) => "'missing required exponent'", + Self::MissingSign(_) => "'missing required `+/-` sign for integer'", + Self::InvalidPositiveSign(_) => "'invalid `+` sign for an integer was found'", + Self::InvalidNegativeSign(_) => "'invalid `-` sign for an unsigned type was found'", + + // NUMBER FORMAT ERRORS + Self::InvalidMantissaRadix => "'invalid radix for mantissa digits'", + Self::InvalidExponentBase => "'invalid exponent base'", + Self::InvalidExponentRadix => "'invalid radix for exponent digits'", + Self::InvalidDigitSeparator => "'invalid digit separator: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidDecimalPoint => "'invalid decimal point: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidExponentSymbol => "'invalid exponent symbol: must be ASCII and not a digit or a `+/-` sign'", + Self::InvalidBasePrefix => "'invalid base prefix character'", + Self::InvalidBaseSuffix => "'invalid base suffix character'", + Self::InvalidPunctuation => "'invalid punctuation: multiple characters overlap'", + Self::InvalidExponentFlags => "'exponent flags set while disabling exponent notation'", + Self::InvalidMantissaSign => "'disabled the `+` sign while requiring a sign for significant digits'", + Self::InvalidExponentSign => "'disabled the `+` sign while requiring a sign for exponent digits'", + Self::InvalidSpecial => "'special flags set while disabling special floats'", + Self::InvalidConsecutiveIntegerDigitSeparator => "'enabled consecutive digit separators in the integer without setting a valid location'", + Self::InvalidConsecutiveFractionDigitSeparator => "'enabled consecutive digit separators in the fraction without setting a valid location'", + Self::InvalidConsecutiveExponentDigitSeparator => "'enabled consecutive digit separators in the exponent without setting a valid location'", + Self::InvalidFlags => "'invalid flags enabled without the format feature'", + + // OPTION ERRORS + Self::InvalidNanString => "'NaN string must started with `n`'", + Self::NanStringTooLong => "'NaN string is too long'", + Self::InvalidInfString => "'short infinity string must started with `i`'", + Self::InfStringTooLong => "'short infinity string is too long'", + Self::InvalidInfinityString => "'long infinity string must started with `i`'", + Self::InfinityStringTooLong => "'long infinity string is too long'", + Self::InfinityStringTooShort => "'long infinity string is too short'", + Self::InvalidFloatParseAlgorithm => "'invalid combination of float parse algorithms'", + Self::InvalidRadix => "'invalid radix for significant digits'", + Self::InvalidFloatPrecision => "'invalid float precision: min digits is larger than max digits'", + Self::InvalidNegativeExponentBreak => "'invalid negative exponent break: value is above 0'", + Self::InvalidPositiveExponentBreak => "'invalid positive exponent break: value is below 0'", + + // NOT AN ERROR + Self::Success => "'not actually an error'", + } + } + /// Get the index for the parsing error. + #[inline] pub fn index(&self) -> Option<&usize> { match self { // PARSE ERRORS @@ -262,84 +323,97 @@ impl Error { /// Add an error message for parsing errors. macro_rules! write_parse_error { - ($formatter:ident, $message:literal, $index:ident) => { + ($formatter:ident, $message:expr, $index:ident) => { write!($formatter, "lexical parse error: {} at index {}", $message, $index) }; } /// Add an error message for number format errors. macro_rules! format_message { - ($formatter:ident, $message:literal) => { + ($formatter:ident, $message:expr) => { write!($formatter, "lexical number format error: {}", $message) }; } /// Add an error message for options errors. macro_rules! options_message { - ($formatter:ident, $message:literal) => { + ($formatter:ident, $message:expr) => { write!($formatter, "lexical options error: {}", $message) }; } impl fmt::Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = self.description(); match self { // PARSE ERRORS - Self::Overflow(index) => write_parse_error!(formatter, "'numeric overflow occurred'", index), - Self::Underflow(index) => write_parse_error!(formatter, "'numeric underflow occurred'", index), - Self::InvalidDigit(index) => write_parse_error!(formatter, "'invalid digit found'", index), - Self::Empty(index) => write_parse_error!(formatter, "'the string to parse was empty'", index), - Self::EmptyMantissa(index) => write_parse_error!(formatter, "'no significant digits found'", index), - Self::EmptyExponent(index) => write_parse_error!(formatter, "'exponent notation found without an exponent'", index), - Self::EmptyInteger(index) => write_parse_error!(formatter, "'invalid float with no integer digits'", index), - Self::EmptyFraction(index) => write_parse_error!(formatter, "'invalid float with no fraction digits'", index), - Self::InvalidPositiveMantissaSign(index) => write_parse_error!(formatter, "'invalid `+` sign before significant digits'", index), - Self::MissingMantissaSign(index) => write_parse_error!(formatter, "'missing required `+/-` sign for significant digits'", index), - Self::InvalidExponent(index) => write_parse_error!(formatter, "'exponent found but not allowed'", index), - Self::InvalidPositiveExponentSign(index) => write_parse_error!(formatter, "'invalid `+` sign in exponent'", index), - Self::MissingExponentSign(index) => write_parse_error!(formatter, "'missing required `+/-` sign for exponent'", index), - Self::ExponentWithoutFraction(index) => write_parse_error!(formatter, "'invalid float containing exponent without fraction'", index), - Self::InvalidLeadingZeros(index) => write_parse_error!(formatter, "'invalid number with leading zeros before digits'", index), - Self::MissingExponent(index) => write_parse_error!(formatter, "'missing required exponent'", index), - Self::MissingSign(index) => write_parse_error!(formatter, "'missing required `+/-` sign for integer'", index), - Self::InvalidPositiveSign(index) => write_parse_error!(formatter, "'invalid `+` sign for an integer was found'", index), - Self::InvalidNegativeSign(index) => write_parse_error!(formatter, "'invalid `-` sign for an unsigned type was found'", index), + Self::Overflow(index) => write_parse_error!(formatter, description, index), + Self::Underflow(index) => write_parse_error!(formatter, description, index), + Self::InvalidDigit(index) => write_parse_error!(formatter, description, index), + Self::Empty(index) => write_parse_error!(formatter, description, index), + Self::EmptyMantissa(index) => write_parse_error!(formatter, description, index), + Self::EmptyExponent(index) => write_parse_error!(formatter, description, index), + Self::EmptyInteger(index) => write_parse_error!(formatter, description, index), + Self::EmptyFraction(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveMantissaSign(index) => { + write_parse_error!(formatter, description, index) + }, + Self::MissingMantissaSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidExponent(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveExponentSign(index) => { + write_parse_error!(formatter, description, index) + }, + Self::MissingExponentSign(index) => write_parse_error!(formatter, description, index), + Self::ExponentWithoutFraction(index) => { + write_parse_error!(formatter, description, index) + }, + Self::InvalidLeadingZeros(index) => write_parse_error!(formatter, description, index), + Self::MissingExponent(index) => write_parse_error!(formatter, description, index), + Self::MissingSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidPositiveSign(index) => write_parse_error!(formatter, description, index), + Self::InvalidNegativeSign(index) => write_parse_error!(formatter, description, index), // NUMBER FORMAT ERRORS - Self::InvalidMantissaRadix => format_message!(formatter, "'invalid radix for mantissa digits'"), - Self::InvalidExponentBase => format_message!(formatter, "'invalid exponent base'"), - Self::InvalidExponentRadix => format_message!(formatter, "'invalid radix for exponent digits'"), - Self::InvalidDigitSeparator => format_message!(formatter, "'invalid digit separator: must be ASCII and not a digit or a `+/-` sign'"), - Self::InvalidDecimalPoint => format_message!(formatter, "'invalid decimal point: must be ASCII and not a digit or a `+/-` sign'"), - Self::InvalidExponentSymbol => format_message!(formatter, "'invalid exponent symbol: must be ASCII and not a digit or a `+/-` sign'"), - Self::InvalidBasePrefix => format_message!(formatter, "'invalid base prefix character'"), - Self::InvalidBaseSuffix => format_message!(formatter, "'invalid base suffix character'"), - Self::InvalidPunctuation => format_message!(formatter, "'invalid punctuation: multiple characters overlap'"), - Self::InvalidExponentFlags => format_message!(formatter, "'exponent flags set while disabling exponent notation'"), - Self::InvalidMantissaSign => format_message!(formatter, "'disabled the `+` sign while requiring a sign for significant digits'"), - Self::InvalidExponentSign => format_message!(formatter, "'disabled the `+` sign while requiring a sign for exponent digits'"), - Self::InvalidSpecial => format_message!(formatter, "'special flags set while disabling special floats'"), - Self::InvalidConsecutiveIntegerDigitSeparator => format_message!(formatter, "'enabled consecutive digit separators in the integer without setting a valid location'"), - Self::InvalidConsecutiveFractionDigitSeparator => format_message!(formatter, "'enabled consecutive digit separators in the fraction without setting a valid location'"), - Self::InvalidConsecutiveExponentDigitSeparator => format_message!(formatter, "'enabled consecutive digit separators in the exponent without setting a valid location'"), - Self::InvalidFlags => format_message!(formatter, "'invalid flags enabled without the format feature'"), + Self::InvalidMantissaRadix => format_message!(formatter, description), + Self::InvalidExponentBase => format_message!(formatter, description), + Self::InvalidExponentRadix => format_message!(formatter, description), + Self::InvalidDigitSeparator => format_message!(formatter, description), + Self::InvalidDecimalPoint => format_message!(formatter, description), + Self::InvalidExponentSymbol => format_message!(formatter, description), + Self::InvalidBasePrefix => format_message!(formatter, description), + Self::InvalidBaseSuffix => format_message!(formatter, description), + Self::InvalidPunctuation => format_message!(formatter, description), + Self::InvalidExponentFlags => format_message!(formatter, description), + Self::InvalidMantissaSign => format_message!(formatter, description), + Self::InvalidExponentSign => format_message!(formatter, description), + Self::InvalidSpecial => format_message!(formatter, description), + Self::InvalidConsecutiveIntegerDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidConsecutiveFractionDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidConsecutiveExponentDigitSeparator => { + format_message!(formatter, description) + }, + Self::InvalidFlags => format_message!(formatter, description), // OPTION ERRORS - Self::InvalidNanString => options_message!(formatter, "'NaN string must started with `n`'"), - Self::NanStringTooLong => options_message!(formatter, "'NaN string is too long'"), - Self::InvalidInfString => options_message!(formatter, "'short infinity string must started with `i`'"), - Self::InfStringTooLong => options_message!(formatter, "'short infinity string is too long'"), - Self::InvalidInfinityString => options_message!(formatter, "'long infinity string must started with `i`'"), - Self::InfinityStringTooLong => options_message!(formatter, "'long infinity string is too long'"), - Self::InfinityStringTooShort => options_message!(formatter, "'long infinity string is too short'"), - Self::InvalidFloatParseAlgorithm => options_message!(formatter, "'invalid combination of float parse algorithms'"), - Self::InvalidRadix => options_message!(formatter, "'invalid radix for significant digits'"), - Self::InvalidFloatPrecision => options_message!(formatter, "'invalid float precision: min digits is larger than max digits'"), - Self::InvalidNegativeExponentBreak => options_message!(formatter, "'invalid negative exponent break: value is above 0'"), - Self::InvalidPositiveExponentBreak => options_message!(formatter, "'invalid positive exponent break: value is below 0'"), + Self::InvalidNanString => options_message!(formatter, description), + Self::NanStringTooLong => options_message!(formatter, description), + Self::InvalidInfString => options_message!(formatter, description), + Self::InfStringTooLong => options_message!(formatter, description), + Self::InvalidInfinityString => options_message!(formatter, description), + Self::InfinityStringTooLong => options_message!(formatter, description), + Self::InfinityStringTooShort => options_message!(formatter, description), + Self::InvalidFloatParseAlgorithm => options_message!(formatter, description), + Self::InvalidRadix => options_message!(formatter, description), + Self::InvalidFloatPrecision => options_message!(formatter, description), + Self::InvalidNegativeExponentBreak => options_message!(formatter, description), + Self::InvalidPositiveExponentBreak => options_message!(formatter, description), // NOT AN ERROR - Self::Success => write!(formatter, "'not actually an error'"), + Self::Success => write!(formatter, "{description}"), } } } diff --git a/lexical-util/src/extended_float.rs b/lexical-util/src/extended_float.rs index c62f3733..46d7e425 100644 --- a/lexical-util/src/extended_float.rs +++ b/lexical-util/src/extended_float.rs @@ -8,6 +8,7 @@ //! this only works for positive floats. #![cfg(feature = "floats")] +#![cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "write-floats"))))] use crate::num::UnsignedInteger; diff --git a/lexical-util/src/feature_format.rs b/lexical-util/src/feature_format.rs index 9f8fe190..aeb06a5d 100644 --- a/lexical-util/src/feature_format.rs +++ b/lexical-util/src/feature_format.rs @@ -697,8 +697,6 @@ use core::num; -use static_assertions::const_assert; - use crate::error::Error; use crate::format_builder::NumberFormatBuilder; use crate::format_flags as flags; @@ -710,11 +708,38 @@ macro_rules! from_flag { }}; } -/// Wrapper for the 128-bit packed struct. +/// Helper to access features from the packed format struct. /// -/// See `NumberFormatBuilder` for the `FORMAT` fields -/// for the packed struct. -#[doc(hidden)] +/// This contains accessory methods to read the formatting settings +/// without using bitmasks directly on the underlying packed struct. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. +/// +/// This should always be constructed via [`NumberFormatBuilder`]. +/// See [`NumberFormatBuilder`] for the fields for the packed struct. +/// +/// # Examples +/// +/// ```rust +/// # #[cfg(feature = "format")] { +/// use lexical_util::format::{RUST_LITERAL, NumberFormat}; +/// +/// let format = NumberFormat::<{ RUST_LITERAL }> {}; +/// assert!(format.no_positive_mantissa_sign()); +/// assert!(format.no_special()); +/// assert!(format.internal_digit_separator()); +/// assert!(format.trailing_digit_separator()); +/// assert!(format.consecutive_digit_separator()); +/// assert!(!format.no_exponent_notation()); +/// # } +/// ``` pub struct NumberFormat; #[rustfmt::skip] @@ -722,6 +747,8 @@ impl NumberFormat { // CONSTRUCTORS /// Create new instance (for methods and validation). + /// + /// This uses the same settings as in the `FORMAT` packed struct. pub const fn new() -> Self { Self {} } @@ -734,211 +761,490 @@ impl NumberFormat { } /// Get the error type from the format. - #[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" + /// + /// If [`Error::Success`] is returned, then no error occurred. pub const fn error(&self) -> Error { - if !flags::is_valid_radix(self.mantissa_radix()) { - Error::InvalidMantissaRadix - } else if !flags::is_valid_radix(self.exponent_base()) { - Error::InvalidExponentBase - } else if !flags::is_valid_radix(self.exponent_radix()) { - Error::InvalidExponentRadix - } else if !flags::is_valid_digit_separator(FORMAT) { - Error::InvalidDigitSeparator - } else if !flags::is_valid_base_prefix(FORMAT) { - Error::InvalidBasePrefix - } else if !flags::is_valid_base_suffix(FORMAT) { - Error::InvalidBaseSuffix - } else if !flags::is_valid_punctuation(FORMAT) { - Error::InvalidPunctuation - } else if !flags::is_valid_exponent_flags(FORMAT) { - Error::InvalidExponentFlags - } else if self.no_positive_mantissa_sign() && self.required_mantissa_sign() { - Error::InvalidMantissaSign - } else if self.no_positive_exponent_sign() && self.required_exponent_sign() { - Error::InvalidExponentSign - } else if self.no_special() && self.case_sensitive_special() { - Error::InvalidSpecial - } else if self.no_special() && self.special_digit_separator() { - Error::InvalidSpecial - } else if self.integer_digit_separator_flags() == flags::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR { - Error::InvalidConsecutiveIntegerDigitSeparator - } else if self.fraction_digit_separator_flags() == flags::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR { - Error::InvalidConsecutiveFractionDigitSeparator - } else if self.exponent_digit_separator_flags() == flags::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR { - Error::InvalidConsecutiveExponentDigitSeparator - } else { - Error::Success - } + format_error_impl(FORMAT) } // NON-DIGIT SEPARATOR FLAGS & MASKS /// If digits are required before the decimal point. + /// + /// See [`required_integer_digits`][Self::required_integer_digits]. pub const REQUIRED_INTEGER_DIGITS: bool = from_flag!(FORMAT, REQUIRED_INTEGER_DIGITS); /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_integer_digits(&self) -> bool { Self::REQUIRED_INTEGER_DIGITS } /// If digits are required after the decimal point. + /// + /// See [`required_fraction_digits`][Self::required_fraction_digits]. pub const REQUIRED_FRACTION_DIGITS: bool = from_flag!(FORMAT, REQUIRED_FRACTION_DIGITS); /// Get if digits are required after the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_fraction_digits(&self) -> bool { Self::REQUIRED_FRACTION_DIGITS } /// If digits are required after the exponent character. + /// + /// See [`required_exponent_digits`][Self::required_exponent_digits]. pub const REQUIRED_EXPONENT_DIGITS: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_DIGITS); /// Get if digits are required after the exponent character. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_exponent_digits(&self) -> bool { Self::REQUIRED_EXPONENT_DIGITS } /// If significant digits are required. + /// + /// See [`required_mantissa_digits`][Self::required_mantissa_digits]. pub const REQUIRED_MANTISSA_DIGITS: bool = from_flag!(FORMAT, REQUIRED_MANTISSA_DIGITS); - /// Get if significant digits are required. + /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_mantissa_digits(&self) -> bool { Self::REQUIRED_MANTISSA_DIGITS } /// If at least 1 digit in the number is required. + /// + /// See [`required_digits`][Self::required_digits]. pub const REQUIRED_DIGITS: bool = from_flag!(FORMAT, REQUIRED_DIGITS); /// Get if at least 1 digit in the number is required. + /// + /// This requires either [`mantissa`] or [`exponent`] digits. + /// + /// [`mantissa`]: Self::required_mantissa_digits + /// [`exponent`]: Self::required_exponent_digits #[inline(always)] pub const fn required_digits(&self) -> bool { Self::REQUIRED_DIGITS } /// If a positive sign before the mantissa is not allowed. + /// + /// See [`no_positive_mantissa_sign`][Self::no_positive_mantissa_sign]. pub const NO_POSITIVE_MANTISSA_SIGN: bool = from_flag!(FORMAT, NO_POSITIVE_MANTISSA_SIGN); /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn no_positive_mantissa_sign(&self) -> bool { Self::NO_POSITIVE_MANTISSA_SIGN } /// If a sign symbol before the mantissa is required. + /// + /// See [`required_mantissa_sign`][Self::required_mantissa_sign]. pub const REQUIRED_MANTISSA_SIGN: bool = from_flag!(FORMAT, REQUIRED_MANTISSA_SIGN); /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn required_mantissa_sign(&self) -> bool { Self::REQUIRED_MANTISSA_SIGN } /// If exponent notation is not allowed. + /// + /// See [`no_exponent_notation`][Self::no_exponent_notation]. pub const NO_EXPONENT_NOTATION: bool = from_flag!(FORMAT, NO_EXPONENT_NOTATION); /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn no_exponent_notation(&self) -> bool { Self::NO_EXPONENT_NOTATION } /// If a positive sign before the exponent is not allowed. + /// + /// See [`no_positive_exponent_sign`][Self::no_positive_exponent_sign]. pub const NO_POSITIVE_EXPONENT_SIGN: bool = from_flag!(FORMAT, NO_POSITIVE_EXPONENT_SIGN); /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn no_positive_exponent_sign(&self) -> bool { Self::NO_POSITIVE_EXPONENT_SIGN } /// If a sign symbol before the exponent is required. + /// + /// See [`required_exponent_sign`][Self::required_exponent_sign]. pub const REQUIRED_EXPONENT_SIGN: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_SIGN); /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn required_exponent_sign(&self) -> bool { Self::REQUIRED_EXPONENT_SIGN } /// If an exponent without fraction is not allowed. + /// + /// See [`no_exponent_without_fraction`][Self::no_exponent_without_fraction]. pub const NO_EXPONENT_WITHOUT_FRACTION: bool = from_flag!(FORMAT, NO_EXPONENT_WITHOUT_FRACTION); /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_exponent_without_fraction(&self) -> bool { Self::NO_EXPONENT_WITHOUT_FRACTION } /// If special (non-finite) values are not allowed. + /// + /// See [`no_special`][Self::no_special]. pub const NO_SPECIAL: bool = from_flag!(FORMAT, NO_SPECIAL); /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_special(&self) -> bool { Self::NO_SPECIAL } /// If special (non-finite) values are case-sensitive. + /// + /// See [`case_sensitive_special`][Self::case_sensitive_special]. pub const CASE_SENSITIVE_SPECIAL: bool = from_flag!(FORMAT, CASE_SENSITIVE_SPECIAL); /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn case_sensitive_special(&self) -> bool { Self::CASE_SENSITIVE_SPECIAL } /// If leading zeros before an integer are not allowed. + /// + /// See [`no_integer_leading_zeros`][Self::no_integer_leading_zeros]. pub const NO_INTEGER_LEADING_ZEROS: bool = from_flag!(FORMAT, NO_INTEGER_LEADING_ZEROS); /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer #[inline(always)] pub const fn no_integer_leading_zeros(&self) -> bool { Self::NO_INTEGER_LEADING_ZEROS } /// If leading zeros before a float are not allowed. + /// + /// See [`no_float_leading_zeros`][Self::no_float_leading_zeros]. pub const NO_FLOAT_LEADING_ZEROS: bool = from_flag!(FORMAT, NO_FLOAT_LEADING_ZEROS); /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_float_leading_zeros(&self) -> bool { Self::NO_FLOAT_LEADING_ZEROS } /// If exponent notation is required. + /// + /// See [`required_exponent_notation`][Self::required_exponent_notation]. pub const REQUIRED_EXPONENT_NOTATION: bool = from_flag!(FORMAT, REQUIRED_EXPONENT_NOTATION); /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn required_exponent_notation(&self) -> bool { Self::REQUIRED_EXPONENT_NOTATION } /// If exponent characters are case-sensitive. + /// + /// See [`case_sensitive_exponent`][Self::case_sensitive_exponent]. pub const CASE_SENSITIVE_EXPONENT: bool = from_flag!(FORMAT, CASE_SENSITIVE_EXPONENT); /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn case_sensitive_exponent(&self) -> bool { Self::CASE_SENSITIVE_EXPONENT } /// If base prefixes are case-sensitive. + /// + /// See [`case_sensitive_base_prefix`][Self::case_sensitive_base_prefix]. pub const CASE_SENSITIVE_BASE_PREFIX: bool = from_flag!(FORMAT, CASE_SENSITIVE_BASE_PREFIX); /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn case_sensitive_base_prefix(&self) -> bool { Self::CASE_SENSITIVE_BASE_PREFIX } /// If base suffixes are case-sensitive. + /// + /// See [`case_sensitive_base_suffix`][Self::case_sensitive_base_suffix]. pub const CASE_SENSITIVE_BASE_SUFFIX: bool = from_flag!(FORMAT, CASE_SENSITIVE_BASE_SUFFIX); /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn case_sensitive_base_suffix(&self) -> bool { Self::CASE_SENSITIVE_BASE_SUFFIX @@ -946,18 +1252,38 @@ impl NumberFormat { // DIGIT SEPARATOR FLAGS & MASKS - // If digit separators are allowed between integer digits. + /// If digit separators are allowed between integer digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`integer_internal_digit_separator`][Self::integer_internal_digit_separator]. pub const INTEGER_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_INTERNAL_DIGIT_SEPARATOR); /// Get if digit separators are allowed between integer digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_internal_digit_separator(&self) -> bool { Self::INTEGER_INTERNAL_DIGIT_SEPARATOR @@ -968,13 +1294,32 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`fraction_internal_digit_separator`][Self::fraction_internal_digit_separator]. pub const FRACTION_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_INTERNAL_DIGIT_SEPARATOR); /// Get if digit separators are allowed between fraction digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_internal_digit_separator(&self) -> bool { Self::FRACTION_INTERNAL_DIGIT_SEPARATOR @@ -985,13 +1330,32 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`exponent_internal_digit_separator`][Self::exponent_internal_digit_separator]. pub const EXPONENT_INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_INTERNAL_DIGIT_SEPARATOR); /// Get if digit separators are allowed between exponent digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_internal_digit_separator(&self) -> bool { Self::EXPONENT_INTERNAL_DIGIT_SEPARATOR @@ -1002,13 +1366,21 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`internal_digit_separator`][Self::internal_digit_separator]. pub const INTERNAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTERNAL_DIGIT_SEPARATOR); /// Get if digit separators are allowed between digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. This is equivalent to any of [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], or + /// [`exponent_internal_digit_separator`] being set. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator #[inline(always)] pub const fn internal_digit_separator(&self) -> bool { Self::INTERNAL_DIGIT_SEPARATOR @@ -1018,12 +1390,32 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`integer_leading_digit_separator`][Self::integer_leading_digit_separator]. pub const INTEGER_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_LEADING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed before any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_leading_digit_separator(&self) -> bool { Self::INTEGER_LEADING_DIGIT_SEPARATOR @@ -1033,12 +1425,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`fraction_leading_digit_separator`][Self::fraction_leading_digit_separator]. pub const FRACTION_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_LEADING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed before any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_leading_digit_separator(&self) -> bool { Self::FRACTION_LEADING_DIGIT_SEPARATOR @@ -1048,12 +1459,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`exponent_leading_digit_separator`][Self::exponent_leading_digit_separator]. pub const EXPONENT_LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_LEADING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed before any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_leading_digit_separator(&self) -> bool { Self::EXPONENT_LEADING_DIGIT_SEPARATOR @@ -1063,12 +1493,21 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`leading_digit_separator`][Self::leading_digit_separator]. pub const LEADING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, LEADING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed before any digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], or + /// [`exponent_leading_digit_separator`] being set. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator #[inline(always)] pub const fn leading_digit_separator(&self) -> bool { Self::LEADING_DIGIT_SEPARATOR @@ -1078,12 +1517,32 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`integer_trailing_digit_separator`][Self::integer_trailing_digit_separator]. pub const INTEGER_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_TRAILING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed after any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_trailing_digit_separator(&self) -> bool { Self::INTEGER_TRAILING_DIGIT_SEPARATOR @@ -1093,12 +1552,29 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`fraction_trailing_digit_separator`][Self::fraction_trailing_digit_separator]. pub const FRACTION_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_TRAILING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed after any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_trailing_digit_separator(&self) -> bool { Self::FRACTION_TRAILING_DIGIT_SEPARATOR @@ -1108,12 +1584,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`exponent_trailing_digit_separator`][Self::exponent_trailing_digit_separator]. pub const EXPONENT_TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_TRAILING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed after any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_trailing_digit_separator(&self) -> bool { Self::EXPONENT_TRAILING_DIGIT_SEPARATOR @@ -1123,57 +1618,121 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`trailing_digit_separator`][Self::trailing_digit_separator]. pub const TRAILING_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, TRAILING_DIGIT_SEPARATOR); /// Get if a digit separator is allowed after any digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], or + /// [`exponent_trailing_digit_separator`] being set. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator #[inline(always)] pub const fn trailing_digit_separator(&self) -> bool { Self::TRAILING_DIGIT_SEPARATOR } /// If multiple consecutive integer digit separators are allowed. + /// + /// See [`integer_consecutive_digit_separator`][Self::integer_consecutive_digit_separator]. pub const INTEGER_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, INTEGER_CONSECUTIVE_DIGIT_SEPARATOR); /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_consecutive_digit_separator(&self) -> bool { Self::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive fraction digit separators are allowed. + /// + /// See [`fraction_consecutive_digit_separator`][Self::fraction_consecutive_digit_separator]. pub const FRACTION_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, FRACTION_CONSECUTIVE_DIGIT_SEPARATOR); /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_consecutive_digit_separator(&self) -> bool { Self::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive exponent digit separators are allowed. + /// + /// See [`exponent_consecutive_digit_separator`][Self::exponent_consecutive_digit_separator]. pub const EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR); /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_consecutive_digit_separator(&self) -> bool { Self::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive digit separators are allowed. + /// + /// See [`consecutive_digit_separator`][Self::consecutive_digit_separator]. pub const CONSECUTIVE_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, CONSECUTIVE_DIGIT_SEPARATOR); /// Get if multiple consecutive digit separators are allowed. + /// + /// This is equivalent to any of [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], or + /// [`exponent_consecutive_digit_separator`] being set. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator #[inline(always)] pub const fn consecutive_digit_separator(&self) -> bool { Self::CONSECUTIVE_DIGIT_SEPARATOR } /// If any digit separators are allowed in special (non-finite) values. + /// + /// See [`special_digit_separator`][Self::special_digit_separator]. pub const SPECIAL_DIGIT_SEPARATOR: bool = from_flag!(FORMAT, SPECIAL_DIGIT_SEPARATOR); /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn special_digit_separator(&self) -> bool { Self::SPECIAL_DIGIT_SEPARATOR @@ -1182,25 +1741,77 @@ impl NumberFormat { // CHARACTERS /// The digit separator character in the packed struct. + /// + /// See [`digit_separator`][Self::digit_separator]. pub const DIGIT_SEPARATOR: u8 = flags::digit_separator(FORMAT); - /// Get the digit separator character. + /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `0`, or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). /// - /// If the digit separator is 0, digit separators are not allowed. + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn digit_separator(&self) -> u8 { Self::DIGIT_SEPARATOR } + /// Get if the format has a digit separator. + #[inline(always)] + pub const fn has_digit_separator(&self) -> bool { + self.digit_separator() != 0 + } + /// The base prefix character in the packed struct. + /// + /// See [`base_prefix`][Self::base_prefix]. pub const BASE_PREFIX: u8 = flags::base_prefix(FORMAT); - /// Get the character for the base prefix. + /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base prefix allowed. + /// + /// # Examples /// - /// If the base prefix is 0, base prefixes are not allowed. - /// The number will have then have the format `0$base_prefix...`. - /// For example, a hex base prefix would be `0x`. Base prefixes are - /// always optional. + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn base_prefix(&self) -> u8 { Self::BASE_PREFIX @@ -1213,14 +1824,32 @@ impl NumberFormat { } /// The base suffix character in the packed struct. + /// + /// See [`base_suffix`][Self::base_suffix]. pub const BASE_SUFFIX: u8 = flags::base_suffix(FORMAT); - /// Character for the base suffix. + /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | /// - /// If not provided, base suffixes are not allowed. - /// The number will have then have the format `...$base_suffix`. - /// For example, a hex base prefix would be `0x`. Base prefixes are - /// always optional. + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn base_suffix(&self) -> u8 { Self::BASE_SUFFIX @@ -1235,37 +1864,63 @@ impl NumberFormat { // RADIX /// The radix for the significant digits in the packed struct. + /// + /// See [`mantissa_radix`][Self::mantissa_radix]. pub const MANTISSA_RADIX: u32 = flags::mantissa_radix(FORMAT); - /// Get the radix for the mantissa digits. + /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer #[inline(always)] pub const fn mantissa_radix(&self) -> u32 { Self::MANTISSA_RADIX } /// The radix for the significant digits in the packed struct. - /// Alias for `MANTISSA_RADIX`. + /// + /// Alias for [`MANTISSA_RADIX`][Self::MANTISSA_RADIX]. pub const RADIX: u32 = Self::MANTISSA_RADIX; /// Get the radix for the significant digits. + /// + /// This is an alias for [`mantissa_radix`][Self::mantissa_radix]. #[inline(always)] pub const fn radix(&self) -> u32 { Self::RADIX } - /// Get the radix**2 for the significant digits. + /// Get the `radix^2` for the significant digits. #[inline(always)] pub const fn radix2(&self) -> u32 { self.radix().wrapping_mul(self.radix()) } - /// Get the radix**4 for the significant digits. + /// Get the `radix^4` for the significant digits. #[inline(always)] pub const fn radix4(&self) -> u32 { self.radix2().wrapping_mul(self.radix2()) } - /// Get the radix*** for the significant digits. + /// Get the `radix^8` for the significant digits. #[inline(always)] pub const fn radix8(&self) -> u32 { // NOTE: radix >= 16 will overflow here but this has no security concerns @@ -1273,23 +1928,52 @@ impl NumberFormat { } /// The base for the exponent. + /// + /// See [`exponent_base`][Self::exponent_base]. pub const EXPONENT_BASE: u32 = flags::exponent_base(FORMAT); - /// Get the base for the exponent. + /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. + /// + /// # Used For /// - /// IE, a base of 2 means we have `mantissa * 2^exponent`. - /// If not provided, it defaults to `radix`. + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn exponent_base(&self) -> u32 { Self::EXPONENT_BASE } /// The radix for the exponent digits. + /// + /// See [`exponent_radix`][Self::exponent_radix]. pub const EXPONENT_RADIX: u32 = flags::exponent_radix(FORMAT); - /// Get the radix for the exponent digits. + /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. /// - /// If not provided, defaults to `radix`. + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn exponent_radix(&self) -> u32 { Self::EXPONENT_RADIX @@ -1298,12 +1982,18 @@ impl NumberFormat { // FLAGS /// Get the flags from the number format. + /// + /// This contains all the non-character and non-radix values + /// in the packed struct. #[inline(always)] pub const fn flags(&self) -> u128 { FORMAT & flags::FLAG_MASK } /// Get the interface flags from the number format. + /// + /// This contains all the flags that dictate code flows, and + /// therefore excludes logic like case-sensitive characters. #[inline(always)] pub const fn interface_flags(&self) -> u128 { FORMAT & flags::INTERFACE_FLAG_MASK @@ -1316,6 +2006,9 @@ impl NumberFormat { } /// Get the exponent flags from the number format. + /// + /// This contains all the flags pertaining to exponent + /// formats, including digit separators. #[inline(always)] pub const fn exponent_flags(&self) -> u128 { FORMAT & flags::EXPONENT_FLAG_MASK @@ -1341,13 +2034,13 @@ impl NumberFormat { // BUILDER - /// Get the number format builder from the format. + /// Get [`NumberFormatBuilder`] as a static function. #[inline(always)] pub const fn builder() -> NumberFormatBuilder { NumberFormatBuilder::new() } - /// Get the number format builder from the format. + /// Create [`NumberFormatBuilder`] using existing values. #[inline(always)] pub const fn rebuild() -> NumberFormatBuilder { NumberFormatBuilder::rebuild(FORMAT) @@ -1360,6 +2053,55 @@ impl Default for NumberFormat { } } +/// Get the error type from the format. +#[inline(always)] +#[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" +pub(crate) const fn format_error_impl(format: u128) -> Error { + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else if !flags::is_valid_digit_separator(format) { + Error::InvalidDigitSeparator + } else if !flags::is_valid_base_prefix(format) { + Error::InvalidBasePrefix + } else if !flags::is_valid_base_suffix(format) { + Error::InvalidBaseSuffix + } else if !flags::is_valid_punctuation(format) { + Error::InvalidPunctuation + } else if !flags::is_valid_exponent_flags(format) { + Error::InvalidExponentFlags + } else if from_flag!(format, NO_POSITIVE_MANTISSA_SIGN) + && from_flag!(format, REQUIRED_MANTISSA_SIGN) + { + Error::InvalidMantissaSign + } else if from_flag!(format, NO_POSITIVE_EXPONENT_SIGN) + && from_flag!(format, REQUIRED_EXPONENT_SIGN) + { + Error::InvalidExponentSign + } else if from_flag!(format, NO_SPECIAL) && from_flag!(format, CASE_SENSITIVE_SPECIAL) { + Error::InvalidSpecial + } else if from_flag!(format, NO_SPECIAL) && from_flag!(format, SPECIAL_DIGIT_SEPARATOR) { + Error::InvalidSpecial + } else if (format & flags::INTEGER_DIGIT_SEPARATOR_FLAG_MASK) + == flags::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveIntegerDigitSeparator + } else if (format & flags::FRACTION_DIGIT_SEPARATOR_FLAG_MASK) + == flags::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveFractionDigitSeparator + } else if (format & flags::EXPONENT_DIGIT_SEPARATOR_FLAG_MASK) + == flags::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR + { + Error::InvalidConsecutiveExponentDigitSeparator + } else { + Error::Success + } +} + // PRE-DEFINED CONSTANTS // --------------------- // @@ -1373,7 +2115,9 @@ impl Default for NumberFormat { // passes test 0, and has no digit separator. // RUST LITERAL [4569ABFGHIJKMN-_] -/// Number format for a `Rust` literal floating-point number. +/// Number format for a [`Rust`] literal floating-point number. +/// +/// [`Rust`]: https://www.rust-lang.org/ #[rustfmt::skip] pub const RUST_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -1383,101 +2127,119 @@ pub const RUST_LITERAL: u128 = NumberFormatBuilder::new() .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ RUST_LITERAL }> {}.is_valid()); + .build_strict(); // RUST STRING [0134567MN] -/// Number format to parse a `Rust` float from string. +/// Number format to parse a [`Rust`] float from string. +/// +/// [`Rust`]: https://www.rust-lang.org/ #[rustfmt::skip] -pub const RUST_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ RUST_STRING }> {}.is_valid()); +pub const RUST_STRING: u128 = NumberFormatBuilder::new().build_strict(); -/// Number format for a `Python` literal floating-point number. +/// Number format for a [`Python`] literal floating-point number. +/// +/// [`Python`]: https://www.python.org/ pub const PYTHON_LITERAL: u128 = PYTHON3_LITERAL; -/// Number format to parse a `Python` float from string. +/// Number format to parse a [`Python`] float from string. +/// +/// [`Python`]: https://www.python.org/ pub const PYTHON_STRING: u128 = PYTHON3_STRING; -/// Number format for a `Python3` literal floating-point number. +/// Number format for a [`Python3`] literal floating-point number. +/// +/// [`Python3`]: https://www.python.org/ pub const PYTHON3_LITERAL: u128 = PYTHON36_LITERAL; // PYTHON3 STRING [0134567MN] -/// Number format to parse a `Python3` float from string. +/// Number format to parse a [`Python3`] float from string. +/// +/// [`Python3`]: https://www.python.org/ #[rustfmt::skip] -pub const PYTHON3_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ PYTHON3_STRING }> {}.is_valid()); +pub const PYTHON3_STRING: u128 = NumberFormatBuilder::new().build_strict(); // PYTHON3.6+ LITERAL [013456N-_] -/// Number format for a `Python3.6` or higher literal floating-point number. +/// Number format for a [`Python3.6`] or higher literal floating-point number. +/// +/// [`Python3.6`]: https://www.python.org/downloads/release/python-360/ #[rustfmt::skip] pub const PYTHON36_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .no_special(true) .no_integer_leading_zeros(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ PYTHON36_LITERAL }> {}.is_valid()); + .build_strict(); // PYTHON3.5- LITERAL [013456N] -/// Number format for a `Python3.5` or lower literal floating-point number. +/// Number format for a [`Python3.5`] or lower literal floating-point number. +/// +/// [`Python3.5`]: https://www.python.org/downloads/release/python-350/ #[rustfmt::skip] pub const PYTHON35_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) .no_integer_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ PYTHON35_LITERAL }> {}.is_valid()); + .build_strict(); // PYTHON2 LITERAL [013456MN] -/// Number format for a `Python2` literal floating-point number. +/// Number format for a [`Python2`] literal floating-point number. +/// +/// [`Python2`]: https://www.python.org/downloads/release/python-270/ #[rustfmt::skip] pub const PYTHON2_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ PYTHON2_LITERAL }> {}.is_valid()); + .build_strict(); // PYTHON2 STRING [0134567MN] -/// Number format to parse a `Python2` float from string. +/// Number format to parse a [`Python2`] float from string. +/// +/// [`Python2`]: https://www.python.org/downloads/release/python-270/ #[rustfmt::skip] -pub const PYTHON2_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ PYTHON2_STRING }> {}.is_valid()); +pub const PYTHON2_STRING: u128 = NumberFormatBuilder::new().build_strict(); -/// Number format for a `C++` literal floating-point number. +/// Number format for a [`C++`] literal floating-point number. +/// +/// [`C++`]: https://en.cppreference.com/w/ pub const CXX_LITERAL: u128 = CXX20_LITERAL; -/// Number format to parse a `C++` float from string. +/// Number format to parse a [`C++`] float from string. +/// +/// [`C++`]: https://en.cppreference.com/w/ pub const CXX_STRING: u128 = CXX20_STRING; -/// Number format for a `C++` literal hexadecimal floating-point number. +/// Number format for a [`C++`] literal hexadecimal floating-point number. +/// +/// [`C++`]: https://en.cppreference.com/w/ #[cfg(feature = "power-of-two")] pub const CXX_HEX_LITERAL: u128 = CXX20_HEX_LITERAL; -/// Number format to parse a `C++` hexadecimal float from string. +/// Number format to parse a [`C++`] hexadecimal float from string. +/// +/// [`C++`]: https://en.cppreference.com/w/ #[cfg(feature = "power-of-two")] pub const CXX_HEX_STRING: u128 = CXX20_HEX_STRING; // C++20 LITERAL [013456789ABMN-'] -/// Number format for a `C++20` literal floating-point number. +/// Number format for a [`C++20`] literal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 #[rustfmt::skip] pub const CXX20_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'\'')) .case_sensitive_special(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ CXX20_LITERAL }> {}.is_valid()); + .build_strict(); // C++20 STRING [0134567MN] -/// Number format for a `C++20` string floating-point number. +/// Number format for a [`C++20`] string floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 #[rustfmt::skip] -pub const CXX20_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX20_STRING }> {}.is_valid()); +pub const CXX20_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C++20 HEX LITERAL [013456789ABMN-'] -/// Number format for a `C++20` literal hexadecimal floating-point number. +/// Number format for a [`C++20`] literal hexadecimal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX20_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -1488,43 +2250,42 @@ pub const CXX20_HEX_LITERAL: u128 = NumberFormatBuilder::new() .exponent_radix(num::NonZeroU8::new(10)) .case_sensitive_special(true) .internal_digit_separator(true) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX20_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // C++20 HEX STRING [0134567MN] -/// Number format for a `C++20` string hexadecimal floating-point number. +/// Number format for a [`C++20`] string hexadecimal floating-point number. +/// +/// [`C++20`]: https://en.cppreference.com/w/cpp/20 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX20_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX20_HEX_STRING }> {}.is_valid()); + .build_strict(); // C++17 LITERAL [013456789ABMN-'] -/// Number format for a `C++17` literal floating-point number. +/// Number format for a [`C++17`] literal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 #[rustfmt::skip] pub const CXX17_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'\'')) .case_sensitive_special(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ CXX17_LITERAL }> {}.is_valid()); + .build_strict(); // C++17 STRING [0134567MN] -/// Number format for a `C++17` string floating-point number. +/// Number format for a [`C++17`] string floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 #[rustfmt::skip] -pub const CXX17_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX17_STRING }> {}.is_valid()); +pub const CXX17_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C++17 HEX LITERAL [013456789ABMN-'] -/// Number format for a `C++17` literal hexadecimal floating-point number. +/// Number format for a [`C++17`] literal hexadecimal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX17_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -1535,143 +2296,152 @@ pub const CXX17_HEX_LITERAL: u128 = NumberFormatBuilder::new() .exponent_radix(num::NonZeroU8::new(10)) .case_sensitive_special(true) .internal_digit_separator(true) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX17_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // C++17 HEX STRING [0134567MN] -/// Number format for a `C++17` string hexadecimal floating-point number. +/// Number format for a [`C++17`] string hexadecimal floating-point number. +/// +/// [`C++17`]: https://en.cppreference.com/w/cpp/17 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX17_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX17_HEX_STRING }> {}.is_valid()); + .build_strict(); // C++14 LITERAL [013456789ABMN-'] -/// Number format for a `C++14` literal floating-point number. +/// Number format for a [`C++14`] literal floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 #[rustfmt::skip] pub const CXX14_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'\'')) .case_sensitive_special(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ CXX14_LITERAL }> {}.is_valid()); + .build_strict(); // C++14 STRING [0134567MN] -/// Number format for a `C++14` string floating-point number. +/// Number format for a [`C++14`] string floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 #[rustfmt::skip] -pub const CXX14_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX14_STRING }> {}.is_valid()); +pub const CXX14_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C++14 HEX STRING [0134567MN] -/// Number format for a `C++14` string hexadecimal floating-point number. +/// Number format for a [`C++14`] string hexadecimal floating-point number. +/// +/// [`C++14`]: https://en.cppreference.com/w/cpp/14 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX14_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX14_HEX_STRING }> {}.is_valid()); + .build_strict(); // C++11 LITERAL [01345678MN] -/// Number format for a `C++11` literal floating-point number. +/// Number format for a [`C++11`] literal floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 #[rustfmt::skip] pub const CXX11_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CXX11_LITERAL }> {}.is_valid()); + .build_strict(); // C++11 STRING [0134567MN] -/// Number format for a `C++11` string floating-point number. +/// Number format for a [`C++11`] string floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 #[rustfmt::skip] -pub const CXX11_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX11_STRING }> {}.is_valid()); +pub const CXX11_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C++11 HEX STRING [0134567MN] -/// Number format for a `C++11` string hexadecimal floating-point number. +/// Number format for a [`C++11`] string hexadecimal floating-point number. +/// +/// [`C++11`]: https://en.cppreference.com/w/cpp/11 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const CXX11_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ CXX11_HEX_STRING }> {}.is_valid()); + .build_strict(); // C++03 LITERAL [01345678MN] -/// Number format for a `C++03` literal floating-point number. +/// Number format for a [`C++03`] literal floating-point number. +/// +/// [`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 #[rustfmt::skip] pub const CXX03_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CXX03_LITERAL }> {}.is_valid()); + .build_strict(); // C++03 STRING [0134567MN] -/// Number format for a `C++03` string floating-point number. +/// Number format for a [`C++03`] string floating-point number. +/// +/// [`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 #[rustfmt::skip] -pub const CXX03_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX03_STRING }> {}.is_valid()); +pub const CXX03_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C++98 LITERAL [01345678MN] -/// Number format for a `C++98` literal floating-point number. +/// Number format for a [`C++98`] literal floating-point number. +/// +/// [`C++98`]: https://en.cppreference.com/w/ #[rustfmt::skip] pub const CXX98_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CXX98_LITERAL }> {}.is_valid()); + .build_strict(); // C++98 STRING [0134567MN] -/// Number format for a `C++98` string floating-point number. +/// Number format for a [`C++98`] string floating-point number. +/// +/// [`C++98`]: https://en.cppreference.com/w/ #[rustfmt::skip] -pub const CXX98_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ CXX98_STRING }> {}.is_valid()); +pub const CXX98_STRING: u128 = NumberFormatBuilder::new().build_strict(); -/// Number format for a C literal floating-point number. +/// Number format for a [`C`] literal floating-point number. +/// +/// [`C`]: https://en.cppreference.com/w/c pub const C_LITERAL: u128 = C18_LITERAL; -/// Number format to parse a `C` float from string. +/// Number format to parse a [`C`] float from string. +/// +/// [`C`]: https://en.cppreference.com/w/c pub const C_STRING: u128 = C18_STRING; -/// Number format for a `C` literal hexadecimal floating-point number. +/// Number format for a [`C`] literal hexadecimal floating-point number. +/// +/// [`C`]: https://en.cppreference.com/w/c #[cfg(feature = "power-of-two")] pub const C_HEX_LITERAL: u128 = C18_HEX_LITERAL; -/// Number format to parse a `C` hexadecimal float from string. +/// Number format to parse a [`C`] hexadecimal float from string. +/// +/// [`C`]: https://en.cppreference.com/w/c #[cfg(feature = "power-of-two")] pub const C_HEX_STRING: u128 = C18_HEX_STRING; // C18 LITERAL [01345678MN] -/// Number format for a `C18` literal floating-point number. +/// Number format for a [`C18`] literal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 #[rustfmt::skip] pub const C18_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ C18_LITERAL }> {}.is_valid()); + .build_strict(); // C18 STRING [0134567MN] -/// Number format for a `C18` string floating-point number. +/// Number format for a [`C18`] string floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 #[rustfmt::skip] -pub const C18_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ C18_STRING }> {}.is_valid()); +pub const C18_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C18 HEX LITERAL [01345678MN] -/// Number format for a `C18` literal hexadecimal floating-point number. +/// Number format for a [`C18`] literal hexadecimal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C18_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -1680,41 +2450,40 @@ pub const C18_HEX_LITERAL: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C18_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // C18 HEX STRING [0134567MN] -/// Number format for a `C18` string hexadecimal floating-point number. +/// Number format for a [`C18`] string hexadecimal floating-point number. +/// +/// [`C18`]: https://en.cppreference.com/w/c/17 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C18_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C18_HEX_STRING }> {}.is_valid()); + .build_strict(); // C11 LITERAL [01345678MN] -/// Number format for a `C11` literal floating-point number. +/// Number format for a [`C11`] literal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 #[rustfmt::skip] pub const C11_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ C11_LITERAL }> {}.is_valid()); + .build_strict(); // C11 STRING [0134567MN] -/// Number format for a `C11` string floating-point number. +/// Number format for a [`C11`] string floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 #[rustfmt::skip] -pub const C11_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ C11_STRING }> {}.is_valid()); +pub const C11_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C11 HEX LITERAL [01345678MN] -/// Number format for a `C11` literal hexadecimal floating-point number. +/// Number format for a [`C11`] literal hexadecimal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C11_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -1723,41 +2492,40 @@ pub const C11_HEX_LITERAL: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C11_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // C11 HEX STRING [0134567MN] -/// Number format for a `C11` string hexadecimal floating-point number. +/// Number format for a [`C11`] string hexadecimal floating-point number. +/// +/// [`C11`]: https://en.cppreference.com/w/c/11 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C11_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C11_HEX_STRING }> {}.is_valid()); + .build_strict(); // C99 LITERAL [01345678MN] -/// Number format for a `C99` literal floating-point number. +/// Number format for a [`C99`] literal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 #[rustfmt::skip] pub const C99_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ C99_LITERAL }> {}.is_valid()); + .build_strict(); // C99 STRING [0134567MN] -/// Number format for a `C99` string floating-point number. +/// Number format for a [`C99`] string floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 #[rustfmt::skip] -pub const C99_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ C99_STRING }> {}.is_valid()); +pub const C99_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C99 HEX LITERAL [01345678MN] -/// Number format for a `C99` literal hexadecimal floating-point number. +/// Number format for a [`C99`] literal hexadecimal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C99_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -1766,82 +2534,80 @@ pub const C99_HEX_LITERAL: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C99_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // C99 HEX STRING [0134567MN] -/// Number format for a `C99` string hexadecimal floating-point number. +/// Number format for a [`C99`] string hexadecimal floating-point number. +/// +/// [`C99`]: https://en.cppreference.com/w/c/99 #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C99_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C99_HEX_STRING }> {}.is_valid()); + .build_strict(); // C90 LITERAL [013456MN] -/// Number format for a `C90` literal floating-point number. +/// Number format for a [`C90`] literal floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c #[rustfmt::skip] pub const C90_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ C90_LITERAL }> {}.is_valid()); + .build_strict(); // C90 STRING [0134567MN] -/// Number format for a `C90` string floating-point number. +/// Number format for a [`C90`] string floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c #[rustfmt::skip] -pub const C90_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ C90_STRING }> {}.is_valid()); +pub const C90_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C90 HEX STRING [0134567MN] -/// Number format for a `C90` string hexadecimal floating-point number. +/// Number format for a [`C90`] string hexadecimal floating-point number. +/// +/// [`C90`]: https://en.cppreference.com/w/c #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C90_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C90_HEX_STRING }> {}.is_valid()); + .build_strict(); // C89 LITERAL [013456MN] -/// Number format for a `C89` literal floating-point number. +/// Number format for a [`C89`] literal floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c #[rustfmt::skip] pub const C89_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ C89_LITERAL }> {}.is_valid()); + .build_strict(); // C89 STRING [0134567MN] -/// Number format for a `C89` string floating-point number. +/// Number format for a [`C89`] string floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c #[rustfmt::skip] -pub const C89_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ C89_STRING }> {}.is_valid()); +pub const C89_STRING: u128 = NumberFormatBuilder::new().build_strict(); // C89 HEX STRING [0134567MN] -/// Number format for a `C89` string hexadecimal floating-point number. +/// Number format for a [`C89`] string hexadecimal floating-point number. +/// +/// [`C89`]: https://en.cppreference.com/w/c #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const C89_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ C89_HEX_STRING }> {}.is_valid()); + .build_strict(); // RUBY LITERAL [345689AMN-_] -/// Number format for a `Ruby` literal floating-point number. +/// Number format for a [`Ruby`] literal floating-point number. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ #[rustfmt::skip] pub const RUBY_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -1851,12 +2617,12 @@ pub const RUBY_LITERAL: u128 = NumberFormatBuilder::new() .no_integer_leading_zeros(true) .no_float_leading_zeros(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ RUBY_LITERAL }> {}.is_valid()); + .build_strict(); // RUBY OCTAL LITERAL [345689AN-_] -/// Number format for a `Ruby` literal floating-point number. +/// Number format for an octal [`Ruby`] literal floating-point number. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const RUBY_OCTAL_LITERAL: u128 = NumberFormatBuilder::new() @@ -1865,25 +2631,24 @@ pub const RUBY_OCTAL_LITERAL: u128 = NumberFormatBuilder::new() .required_digits(true) .no_special(true) .internal_digit_separator(true) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ RUBY_OCTAL_LITERAL }> {}.is_valid()); + .build_strict(); // RUBY STRING [01234569ABMN-_] // Note: Amazingly, Ruby 1.8+ do not allow parsing special values. -/// Number format to parse a `Ruby` float from string. +/// Number format to parse a [`Ruby`] float from string. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ #[rustfmt::skip] pub const RUBY_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .no_special(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ RUBY_STRING }> {}.is_valid()); + .build_strict(); // SWIFT LITERAL [34569ABFGHIJKMN-_] -/// Number format for a `Swift` literal floating-point number. +/// Number format for a [`Swift`] literal floating-point number. +/// +/// [`Swift`]: https://developer.apple.com/swift/ #[rustfmt::skip] pub const SWIFT_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -1892,82 +2657,82 @@ pub const SWIFT_LITERAL: u128 = NumberFormatBuilder::new() .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ SWIFT_LITERAL }> {}.is_valid()); + .build_strict(); // SWIFT STRING [134567MN] -/// Number format to parse a `Swift` float from string. +/// Number format to parse a [`Swift`] float from string. +/// +/// [`Swift`]: https://developer.apple.com/swift/ #[rustfmt::skip] pub const SWIFT_STRING: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) - .build(); - -const_assert!(NumberFormat::<{ SWIFT_STRING }> {}.is_valid()); + .build_strict(); // GO LITERAL [13456MN] -/// Number format for a `Golang` literal floating-point number. +/// Number format for a [`Golang`] literal floating-point number. +/// +/// [`Golang`]: https://go.dev/ #[rustfmt::skip] pub const GO_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ GO_LITERAL }> {}.is_valid()); + .build_strict(); // GO STRING [134567MN] -/// Number format to parse a `Golang` float from string. +/// Number format to parse a [`Golang`] float from string. +/// +/// [`Golang`]: https://go.dev/ #[rustfmt::skip] pub const GO_STRING: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) - .build(); - -const_assert!(NumberFormat::<{ GO_STRING }> {}.is_valid()); + .build_strict(); // HASKELL LITERAL [456MN] -/// Number format for a `Haskell` literal floating-point number. +/// Number format for a [`Haskell`] literal floating-point number. +/// +/// [`Haskell`]: https://www.haskell.org/ #[rustfmt::skip] pub const HASKELL_LITERAL: u128 = NumberFormatBuilder::new() .required_digits(true) .no_positive_mantissa_sign(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ HASKELL_LITERAL }> {}.is_valid()); + .build_strict(); // HASKELL STRING [45678MN] -/// Number format to parse a `Haskell` float from string. +/// Number format to parse a [`Haskell`] float from string. +/// +/// [`Haskell`]: https://www.haskell.org/ #[rustfmt::skip] pub const HASKELL_STRING: u128 = NumberFormatBuilder::new() .required_digits(true) .no_positive_mantissa_sign(true) .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ HASKELL_STRING }> {}.is_valid()); + .build_strict(); // JAVASCRIPT LITERAL [01345678M] -/// Number format for a `Javascript` literal floating-point number. +/// Number format for a [`Javascript`] literal floating-point number. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript #[rustfmt::skip] pub const JAVASCRIPT_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) .no_float_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ JAVASCRIPT_LITERAL }> {}.is_valid()); + .build_strict(); // JAVASCRIPT STRING [012345678MN] -/// Number format to parse a `Javascript` float from string. +/// Number format to parse a [`Javascript`] float from string. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript #[rustfmt::skip] pub const JAVASCRIPT_STRING: u128 = NumberFormatBuilder::new() .required_exponent_digits(false) .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ JAVASCRIPT_STRING }> {}.is_valid()); + .build_strict(); // PERL LITERAL [0134569ABDEFGHIJKMN-_] -/// Number format for a `Perl` literal floating-point number. +/// Number format for a [`Perl`] literal floating-point number. +/// +/// [`Perl`]: https://www.perl.org/ #[rustfmt::skip] pub const PERL_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -1977,69 +2742,73 @@ pub const PERL_LITERAL: u128 = NumberFormatBuilder::new() .exponent_leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ PERL_LITERAL }> {}.is_valid()); + .build_strict(); // PERL STRING [01234567MN] -/// Number format to parse a `Perl` float from string. +/// Number format to parse a [`Perl`] float from string. +/// +/// [`Perl`]: https://www.perl.org/ pub const PERL_STRING: u128 = PERMISSIVE; // PHP LITERAL [01345678MN] -/// Number format for a `PHP` literal floating-point number. +/// Number format for a [`PHP`] literal floating-point number. +/// +/// [`PHP`]: https://www.php.net/ #[rustfmt::skip] pub const PHP_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ PHP_LITERAL }> {}.is_valid()); + .build_strict(); // PHP STRING [0123456MN] -/// Number format to parse a `PHP` float from string. +/// Number format to parse a [`PHP`] float from string. +/// +/// [`PHP`]: https://www.php.net/ #[rustfmt::skip] pub const PHP_STRING: u128 = NumberFormatBuilder::new() .required_exponent_digits(false) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ PHP_STRING }> {}.is_valid()); + .build_strict(); // JAVA LITERAL [0134569ABIJKMN-_] -/// Number format for a `Java` literal floating-point number. +/// Number format for a [`Java`] literal floating-point number. +/// +/// [`Java`]: https://www.java.com/en/ #[rustfmt::skip] pub const JAVA_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .no_special(true) .internal_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ JAVA_LITERAL }> {}.is_valid()); + .build_strict(); // JAVA STRING [01345678MN] -/// Number format to parse a `Java` float from string. +/// Number format to parse a [`Java`] float from string. +/// +/// [`Java`]: https://www.java.com/en/ #[rustfmt::skip] pub const JAVA_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ JAVA_STRING }> {}.is_valid()); + .build_strict(); // R LITERAL [01345678MN] -/// Number format for a `R` literal floating-point number. +/// Number format for an [`R`] literal floating-point number. +/// +/// [`R`]: https://www.r-project.org/ #[rustfmt::skip] pub const R_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ R_LITERAL }> {}.is_valid()); + .build_strict(); // R STRING [01234567MN] -/// Number format to parse a `R` float from string. +/// Number format to parse an [`R`] float from string. +/// +/// [`R`]: https://www.r-project.org/ pub const R_STRING: u128 = PERMISSIVE; // KOTLIN LITERAL [0134569ABIJKN-_] -/// Number format for a `Kotlin` literal floating-point number. +/// Number format for a [`Kotlin`] literal floating-point number. +/// +/// [`Kotlin`]: https://kotlinlang.org/ #[rustfmt::skip] pub const KOTLIN_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2047,39 +2816,40 @@ pub const KOTLIN_LITERAL: u128 = NumberFormatBuilder::new() .no_integer_leading_zeros(true) .internal_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ KOTLIN_LITERAL }> {}.is_valid()); + .build_strict(); // KOTLIN STRING [0134568MN] -/// Number format to parse a `Kotlin` float from string. +/// Number format to parse a [`Kotlin`] float from string. +/// +/// [`Kotlin`]: https://kotlinlang.org/ #[rustfmt::skip] pub const KOTLIN_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ KOTLIN_STRING }> {}.is_valid()); + .build_strict(); // JULIA LITERAL [01345689AMN-_] -/// Number format for a `Julia` literal floating-point number. +/// Number format for a [`Julia`] literal floating-point number. +/// +/// [`Julia`]: https://julialang.org/ #[rustfmt::skip] pub const JULIA_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .case_sensitive_special(true) .integer_internal_digit_separator(true) .fraction_internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ JULIA_LITERAL }> {}.is_valid()); + .build_strict(); // JULIA STRING [01345678MN] -/// Number format to parse a `Julia` float from string. +/// Number format to parse a [`Julia`] float from string. +/// +/// [`Julia`]: https://julialang.org/ #[rustfmt::skip] -pub const JULIA_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ JULIA_STRING }> {}.is_valid()); +pub const JULIA_STRING: u128 = NumberFormatBuilder::new().build_strict(); // JULIA HEX LITERAL [01345689AMN-_] -/// Number format for a `Julia` literal floating-point number. +/// Number format for a [`Julia`] literal floating-point number. +/// +/// [`Julia`]: https://julialang.org/ #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const JULIA_HEX_LITERAL: u128 = NumberFormatBuilder::new() @@ -2090,32 +2860,34 @@ pub const JULIA_HEX_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) .integer_internal_digit_separator(true) .fraction_internal_digit_separator(true) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ JULIA_HEX_LITERAL }> {}.is_valid()); + .build_strict(); // JULIA HEX STRING [01345678MN] -/// Number format to parse a `Julia` float from string. +/// Number format to parse a [`Julia`] float from string. +/// +/// [`Julia`]: https://julialang.org/ #[rustfmt::skip] #[cfg(feature = "power-of-two")] pub const JULIA_HEX_STRING: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); - -#[cfg(feature = "power-of-two")] -const_assert!(NumberFormat::<{ JULIA_HEX_STRING }> {}.is_valid()); + .build_strict(); -/// Number format for a `C#` literal floating-point number. +/// Number format for a [`C#`] literal floating-point number. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ pub const CSHARP_LITERAL: u128 = CSHARP7_LITERAL; -/// Number format to parse a `C#` float from string. +/// Number format to parse a [`C#`] float from string. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ pub const CSHARP_STRING: u128 = CSHARP7_STRING; // CSHARP7 LITERAL [034569ABIJKMN-_] -/// Number format for a `C#7` literal floating-point number. +/// Number format for a [`C#7`] literal floating-point number. +/// +/// [`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 #[rustfmt::skip] pub const CSHARP7_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2123,230 +2895,230 @@ pub const CSHARP7_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) .internal_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP7_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP7 STRING [0134568MN] -/// Number format to parse a `C#7` float from string. +/// Number format to parse a [`C#7`] float from string. +/// +/// [`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 #[rustfmt::skip] pub const CSHARP7_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP7_STRING }> {}.is_valid()); + .build_strict(); // CSHARP6 LITERAL [03456MN] -/// Number format for a `C#6` literal floating-point number. +/// Number format for a [`C#6`] literal floating-point number. +/// +/// [`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 #[rustfmt::skip] pub const CSHARP6_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP6_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP6 STRING [0134568MN] -/// Number format to parse a `C#6` float from string. +/// Number format to parse a [`C#6`] float from string. +/// +/// [`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 #[rustfmt::skip] pub const CSHARP6_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP6_STRING }> {}.is_valid()); + .build_strict(); // CSHARP5 LITERAL [03456MN] -/// Number format for a `C#5` literal floating-point number. +/// Number format for a [`C#5`] literal floating-point number. +/// +/// [`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 #[rustfmt::skip] pub const CSHARP5_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP5_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP5 STRING [0134568MN] -/// Number format to parse a `C#5` float from string. +/// Number format to parse a [`C#5`] float from string. +/// +/// [`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 #[rustfmt::skip] pub const CSHARP5_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP5_STRING }> {}.is_valid()); + .build_strict(); // CSHARP4 LITERAL [03456MN] -/// Number format for a `C#4` literal floating-point number. +/// Number format for a [`C#4`] literal floating-point number. +/// +/// [`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 #[rustfmt::skip] pub const CSHARP4_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP4_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP4 STRING [0134568MN] -/// Number format to parse a `C#4` float from string. +/// Number format to parse a [`C#4`] float from string. +/// +/// [`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 #[rustfmt::skip] pub const CSHARP4_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP4_STRING }> {}.is_valid()); + .build_strict(); // CSHARP3 LITERAL [03456MN] -/// Number format for a `C#3` literal floating-point number. +/// Number format for a [`C#3`] literal floating-point number. +/// +/// [`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 #[rustfmt::skip] pub const CSHARP3_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP3_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP3 STRING [0134568MN] -/// Number format to parse a `C#3` float from string. +/// Number format to parse a [`C#3`] float from string. +/// +/// [`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 #[rustfmt::skip] pub const CSHARP3_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP3_STRING }> {}.is_valid()); + .build_strict(); // CSHARP2 LITERAL [03456MN] -/// Number format for a `C#2` literal floating-point number. +/// Number format for a [`C#2`] literal floating-point number. +/// +/// [`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 #[rustfmt::skip] pub const CSHARP2_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP2_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP2 STRING [0134568MN] -/// Number format to parse a `C#2` float from string. +/// Number format to parse a [`C#2`] float from string. +/// +/// [`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 #[rustfmt::skip] pub const CSHARP2_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP2_STRING }> {}.is_valid()); + .build_strict(); // CSHARP1 LITERAL [03456MN] -/// Number format for a `C#1` literal floating-point number. +/// Number format for a [`C#1`] literal floating-point number. +/// +/// [`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 #[rustfmt::skip] pub const CSHARP1_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP1_LITERAL }> {}.is_valid()); + .build_strict(); // CSHARP1 STRING [0134568MN] -/// Number format to parse a `C#1` float from string. +/// Number format to parse a [`C#1`] float from string. +/// +/// [`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 #[rustfmt::skip] pub const CSHARP1_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CSHARP1_STRING }> {}.is_valid()); + .build_strict(); // KAWA LITERAL [013456MN] -/// Number format for a `Kawa` literal floating-point number. +/// Number format for a [`Kawa`] literal floating-point number. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ #[rustfmt::skip] pub const KAWA_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ KAWA_LITERAL }> {}.is_valid()); + .build_strict(); // KAWA STRING [013456MN] -/// Number format to parse a `Kawa` float from string. +/// Number format to parse a [`Kawa`] float from string. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ #[rustfmt::skip] pub const KAWA_STRING: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ KAWA_STRING }> {}.is_valid()); + .build_strict(); // GAMBITC LITERAL [013456MN] -/// Number format for a `Gambit-C` literal floating-point number. +/// Number format for a [`Gambit-C`] literal floating-point number. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ #[rustfmt::skip] pub const GAMBITC_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ GAMBITC_LITERAL }> {}.is_valid()); + .build_strict(); // GAMBITC STRING [013456MN] -/// Number format to parse a `Gambit-C` float from string. +/// Number format to parse a [`Gambit-C`] float from string. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ #[rustfmt::skip] pub const GAMBITC_STRING: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ GAMBITC_STRING }> {}.is_valid()); + .build_strict(); // GUILE LITERAL [013456MN] -/// Number format for a `Guile` literal floating-point number. +/// Number format for a [`Guile`] literal floating-point number. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ #[rustfmt::skip] pub const GUILE_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ GUILE_LITERAL }> {}.is_valid()); + .build_strict(); // GUILE STRING [013456MN] -/// Number format to parse a `Guile` float from string. +/// Number format to parse a [`Guile`] float from string. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ #[rustfmt::skip] pub const GUILE_STRING: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ GUILE_STRING }> {}.is_valid()); + .build_strict(); // CLOJURE LITERAL [13456MN] -/// Number format for a `Clojure` literal floating-point number. +/// Number format for a [`Clojure`] literal floating-point number. +/// +/// [`Clojure`]: https://clojure.org/ #[rustfmt::skip] pub const CLOJURE_LITERAL: u128 = NumberFormatBuilder::new() .required_integer_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ CLOJURE_LITERAL }> {}.is_valid()); + .build_strict(); // CLOJURE STRING [01345678MN] -/// Number format to parse a `Clojure` float from string. +/// Number format to parse a [`Clojure`] float from string. +/// +/// [`Clojure`]: https://clojure.org/ #[rustfmt::skip] pub const CLOJURE_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ CLOJURE_STRING }> {}.is_valid()); + .build_strict(); // ERLANG LITERAL [34578MN] -/// Number format for an `Erlang` literal floating-point number. +/// Number format for an [`Erlang`] literal floating-point number. +/// +/// [`Erlang`]: https://www.erlang.org/ #[rustfmt::skip] pub const ERLANG_LITERAL: u128 = NumberFormatBuilder::new() .required_digits(true) .no_exponent_without_fraction(true) .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ ERLANG_LITERAL }> {}.is_valid()); + .build_strict(); // ERLANG STRING [345MN] -/// Number format to parse an `Erlang` float from string. +/// Number format to parse an [`Erlang`] float from string. +/// +/// [`Erlang`]: https://www.erlang.org/ #[rustfmt::skip] pub const ERLANG_STRING: u128 = NumberFormatBuilder::new() .required_digits(true) .no_exponent_without_fraction(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ ERLANG_STRING }> {}.is_valid()); + .build_strict(); // ELM LITERAL [456] -/// Number format for an `Elm` literal floating-point number. +/// Number format for an [`Elm`] literal floating-point number. +/// +/// [`Elm`]: https://elm-lang.org/ #[rustfmt::skip] pub const ELM_LITERAL: u128 = NumberFormatBuilder::new() .required_digits(true) @@ -2354,43 +3126,43 @@ pub const ELM_LITERAL: u128 = NumberFormatBuilder::new() .no_integer_leading_zeros(true) .no_float_leading_zeros(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ ELM_LITERAL }> {}.is_valid()); + .build_strict(); // ELM STRING [01345678MN] // Note: There is no valid representation of NaN, just Infinity. -/// Number format to parse an `Elm` float from string. +/// Number format to parse an [`Elm`] float from string. +/// +/// [`Elm`]: https://elm-lang.org/ #[rustfmt::skip] pub const ELM_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ ELM_STRING }> {}.is_valid()); + .build_strict(); // SCALA LITERAL [3456] -/// Number format for a `Scala` literal floating-point number. +/// Number format for a [`Scala`] literal floating-point number. +/// +/// [`Scala`]: https://www.scala-lang.org/ #[rustfmt::skip] pub const SCALA_LITERAL: u128 = NumberFormatBuilder::new() .required_digits(true) .no_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ SCALA_LITERAL }> {}.is_valid()); + .build_strict(); // SCALA STRING [01345678MN] -/// Number format to parse a `Scala` float from string. +/// Number format to parse a [`Scala`] float from string. +/// +/// [`Scala`]: https://www.scala-lang.org/ #[rustfmt::skip] pub const SCALA_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ SCALA_STRING }> {}.is_valid()); + .build_strict(); // ELIXIR LITERAL [3459ABMN-_] -/// Number format for an `Elixir` literal floating-point number. +/// Number format for an [`Elixir`] literal floating-point number. +/// +/// [`Elixir`]: https://elixir-lang.org/ #[rustfmt::skip] pub const ELIXIR_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2398,38 +3170,39 @@ pub const ELIXIR_LITERAL: u128 = NumberFormatBuilder::new() .no_exponent_without_fraction(true) .no_special(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ ELIXIR_LITERAL }> {}.is_valid()); + .build_strict(); // ELIXIR STRING [345MN] -/// Number format to parse an `Elixir` float from string. +/// Number format to parse an [`Elixir`] float from string. +/// +/// [`Elixir`]: https://elixir-lang.org/ #[rustfmt::skip] pub const ELIXIR_STRING: u128 = NumberFormatBuilder::new() .required_digits(true) .no_exponent_without_fraction(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ ELIXIR_STRING }> {}.is_valid()); + .build_strict(); // FORTRAN LITERAL [013456MN] -/// Number format for a `FORTRAN` literal floating-point number. +/// Number format for a [`FORTRAN`] literal floating-point number. +/// +/// [`FORTRAN`]: https://fortran-lang.org/ #[rustfmt::skip] pub const FORTRAN_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ FORTRAN_LITERAL }> {}.is_valid()); + .build_strict(); // FORTRAN STRING [0134567MN] -/// Number format to parse a `FORTRAN` float from string. +/// Number format to parse a [`FORTRAN`] float from string. +/// +/// [`FORTRAN`]: https://fortran-lang.org/ #[rustfmt::skip] -pub const FORTRAN_STRING: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ FORTRAN_STRING }> {}.is_valid()); +pub const FORTRAN_STRING: u128 = NumberFormatBuilder::new().build_strict(); // D LITERAL [0134569ABFGHIJKN-_] -/// Number format for a `D` literal floating-point number. +/// Number format for a [`D`] literal floating-point number. +/// +/// [`D`]: https://dlang.org/ #[rustfmt::skip] pub const D_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2438,12 +3211,12 @@ pub const D_LITERAL: u128 = NumberFormatBuilder::new() .internal_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ D_LITERAL }> {}.is_valid()); + .build_strict(); // D STRING [01345679AFGMN-_] -/// Number format to parse a `D` float from string. +/// Number format to parse a [`D`] float from string. +/// +/// [`D`]: https://dlang.org/ #[rustfmt::skip] pub const D_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2451,53 +3224,53 @@ pub const D_STRING: u128 = NumberFormatBuilder::new() .fraction_internal_digit_separator(true) .integer_trailing_digit_separator(true) .fraction_trailing_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ D_STRING }> {}.is_valid()); + .build_strict(); // COFFEESCRIPT LITERAL [01345678] -/// Number format for a `Coffeescript` literal floating-point number. +/// Number format for a [`Coffeescript`] literal floating-point number. +/// +/// [`Coffeescript`]: https://coffeescript.org/ #[rustfmt::skip] pub const COFFEESCRIPT_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ COFFEESCRIPT_LITERAL }> {}.is_valid()); + .build_strict(); // COFFEESCRIPT STRING [012345678MN] -/// Number format to parse a `Coffeescript` float from string. +/// Number format to parse a [`Coffeescript`] float from string. +/// +/// [`Coffeescript`]: https://coffeescript.org/ #[rustfmt::skip] pub const COFFEESCRIPT_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ COFFEESCRIPT_STRING }> {}.is_valid()); + .build_strict(); // COBOL LITERAL [0345MN] -/// Number format for a `Cobol` literal floating-point number. +/// Number format for a [`Cobol`] literal floating-point number. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol #[rustfmt::skip] pub const COBOL_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_exponent_without_fraction(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ COBOL_LITERAL }> {}.is_valid()); + .build_strict(); // COBOL STRING [012356MN] -/// Number format to parse a `Cobol` float from string. +/// Number format to parse a [`Cobol`] float from string. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol #[rustfmt::skip] pub const COBOL_STRING: u128 = NumberFormatBuilder::new() .required_exponent_sign(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ COBOL_STRING }> {}.is_valid()); + .build_strict(); // FSHARP LITERAL [13456789ABIJKMN-_] -/// Number format for a `F#` literal floating-point number. +/// Number format for a [`F#`] literal floating-point number. +/// +/// [`F#`]: https://fsharp.org/ #[rustfmt::skip] pub const FSHARP_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2506,12 +3279,12 @@ pub const FSHARP_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) .internal_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ FSHARP_LITERAL }> {}.is_valid()); + .build_strict(); // FSHARP STRING [013456789ABCDEFGHIJKLMN-_] -/// Number format to parse a `F#` float from string. +/// Number format to parse a [`F#`] float from string. +/// +/// [`F#`]: https://fsharp.org/ #[rustfmt::skip] pub const FSHARP_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2520,32 +3293,32 @@ pub const FSHARP_STRING: u128 = NumberFormatBuilder::new() .trailing_digit_separator(true) .consecutive_digit_separator(true) .special_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ FSHARP_STRING }> {}.is_valid()); + .build_strict(); // VB LITERAL [03456MN] -/// Number format for a `Visual Basic` literal floating-point number. +/// Number format for a [`Visual Basic`] literal floating-point number. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ #[rustfmt::skip] pub const VB_LITERAL: u128 = NumberFormatBuilder::new() .required_fraction_digits(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ VB_LITERAL }> {}.is_valid()); + .build_strict(); // VB STRING [01345678MN] -/// Number format to parse a `Visual Basic` float from string. +/// Number format to parse a [`Visual Basic`] float from string. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ // Note: To my knowledge, Visual Basic cannot parse infinity. #[rustfmt::skip] pub const VB_STRING: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ VB_STRING }> {}.is_valid()); + .build_strict(); // OCAML LITERAL [1456789ABDFGHIJKMN-_] -/// Number format for an `OCaml` literal floating-point number. +/// Number format for an [`OCaml`] literal floating-point number. +/// +/// [`OCaml`]: https://ocaml.org/ #[rustfmt::skip] pub const OCAML_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2557,12 +3330,12 @@ pub const OCAML_LITERAL: u128 = NumberFormatBuilder::new() .fraction_leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ OCAML_LITERAL }> {}.is_valid()); + .build_strict(); // OCAML STRING [01345679ABCDEFGHIJKLMN-_] -/// Number format to parse an `OCaml` float from string. +/// Number format to parse an [`OCaml`] float from string. +/// +/// [`OCaml`]: https://ocaml.org/ #[rustfmt::skip] pub const OCAML_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2571,30 +3344,30 @@ pub const OCAML_STRING: u128 = NumberFormatBuilder::new() .trailing_digit_separator(true) .consecutive_digit_separator(true) .special_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ OCAML_STRING }> {}.is_valid()); + .build_strict(); // OBJECTIVEC LITERAL [013456MN] -/// Number format for an `Objective-C` literal floating-point number. +/// Number format for an [`Objective-C`] literal floating-point number. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C #[rustfmt::skip] pub const OBJECTIVEC_LITERAL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ OBJECTIVEC_LITERAL }> {}.is_valid()); + .build_strict(); // OBJECTIVEC STRING [013456MN] -/// Number format to parse an `Objective-C` float from string. +/// Number format to parse an [`Objective-C`] float from string. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C #[rustfmt::skip] pub const OBJECTIVEC_STRING: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ OBJECTIVEC_STRING }> {}.is_valid()); + .build_strict(); // REASONML LITERAL [13456789ABDFGHIJKMN-_] -/// Number format for a `ReasonML` literal floating-point number. +/// Number format for a [`ReasonML`] literal floating-point number. +/// +/// [`ReasonML`]: https://reasonml.github.io/ #[rustfmt::skip] pub const REASONML_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2605,12 +3378,12 @@ pub const REASONML_LITERAL: u128 = NumberFormatBuilder::new() .fraction_leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ REASONML_LITERAL }> {}.is_valid()); + .build_strict(); // REASONML STRING [01345679ABCDEFGHIJKLMN-_] -/// Number format to parse a `ReasonML` float from string. +/// Number format to parse a [`ReasonML`] float from string. +/// +/// [`ReasonML`]: https://reasonml.github.io/ #[rustfmt::skip] pub const REASONML_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2619,13 +3392,13 @@ pub const REASONML_STRING: u128 = NumberFormatBuilder::new() .trailing_digit_separator(true) .consecutive_digit_separator(true) .special_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ REASONML_STRING }> {}.is_valid()); + .build_strict(); // OCTAVE LITERAL [013456789ABDFGHIJKMN-_] // Note: Octave accepts both NaN and nan, Inf and inf. -/// Number format for an `Octave` literal floating-point number. +/// Number format for an [`Octave`] literal floating-point number. +/// +/// [`Octave`]: https://octave.org/ #[rustfmt::skip] pub const OCTAVE_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2634,12 +3407,12 @@ pub const OCTAVE_LITERAL: u128 = NumberFormatBuilder::new() .fraction_leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ OCTAVE_LITERAL }> {}.is_valid()); + .build_strict(); // OCTAVE STRING [01345679ABCDEFGHIJKMN-,] -/// Number format to parse an `Octave` float from string. +/// Number format to parse an [`Octave`] float from string. +/// +/// [`Octave`]: https://octave.org/ #[rustfmt::skip] pub const OCTAVE_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b',')) @@ -2647,13 +3420,13 @@ pub const OCTAVE_STRING: u128 = NumberFormatBuilder::new() .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ OCTAVE_STRING }> {}.is_valid()); + .build_strict(); // MATLAB LITERAL [013456789ABDFGHIJKMN-_] // Note: Matlab accepts both NaN and nan, Inf and inf. -/// Number format for an `Matlab` literal floating-point number. +/// Number format for an [`Matlab`] literal floating-point number. +/// +/// [`Matlab`]: https://www.mathworks.com/products/matlab.html #[rustfmt::skip] pub const MATLAB_LITERAL: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2662,12 +3435,12 @@ pub const MATLAB_LITERAL: u128 = NumberFormatBuilder::new() .fraction_leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ MATLAB_LITERAL }> {}.is_valid()); + .build_strict(); // MATLAB STRING [01345679ABCDEFGHIJKMN-,] -/// Number format to parse an `Matlab` float from string. +/// Number format to parse an [`Matlab`] float from string. +/// +/// [`Matlab`]: https://www.mathworks.com/products/matlab.html #[rustfmt::skip] pub const MATLAB_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b',')) @@ -2675,47 +3448,49 @@ pub const MATLAB_STRING: u128 = NumberFormatBuilder::new() .leading_digit_separator(true) .trailing_digit_separator(true) .consecutive_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ MATLAB_STRING }> {}.is_valid()); + .build_strict(); // ZIG LITERAL [1456MN] -/// Number format for a `Zig` literal floating-point number. +/// Number format for a [`Zig`] literal floating-point number. +/// +/// [`Zig`]: https://ziglang.org/ #[rustfmt::skip] pub const ZIG_LITERAL: u128 = NumberFormatBuilder::new() .required_integer_digits(true) .no_positive_mantissa_sign(true) .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ ZIG_LITERAL }> {}.is_valid()); + .build_strict(); // ZIG STRING [01234567MN] -/// Number format to parse a `Zig` float from string. +/// Number format to parse a [`Zig`] float from string. +/// +/// [`Zig`]: https://ziglang.org/ pub const ZIG_STRING: u128 = PERMISSIVE; // SAGE LITERAL [012345678MN] // Note: Both Infinity and infinity are accepted. -/// Number format for a `Sage` literal floating-point number. +/// Number format for a [`Sage`] literal floating-point number. +/// +/// [`Sage`]: https://www.sagemath.org/ #[rustfmt::skip] pub const SAGE_LITERAL: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ SAGE_LITERAL }> {}.is_valid()); + .build_strict(); // SAGE STRING [01345679ABMN-_] -/// Number format to parse a `Sage` float from string. +/// Number format to parse a [`Sage`] float from string. +/// +/// [`Sage`]: https://www.sagemath.org/ #[rustfmt::skip] pub const SAGE_STRING: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ SAGE_STRING }> {}.is_valid()); + .build_strict(); // JSON [456] -/// Number format for a `JSON` literal floating-point number. +/// Number format for a [`JSON`][`JSON-REF`] literal floating-point number. +/// +/// [`JSON-REF`]: https://www.json.org/json-en.html #[rustfmt::skip] pub const JSON: u128 = NumberFormatBuilder::new() .required_digits(true) @@ -2723,12 +3498,12 @@ pub const JSON: u128 = NumberFormatBuilder::new() .no_special(true) .no_integer_leading_zeros(true) .no_float_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ JSON }> {}.is_valid()); + .build_strict(); // TOML [34569AB] -/// Number format for a `TOML` literal floating-point number. +/// Number format for a [`TOML`][`TOML-REF`] literal floating-point number. +/// +/// [`TOML-REF`]: https://toml.io/en/ #[rustfmt::skip] pub const TOML: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) @@ -2737,60 +3512,60 @@ pub const TOML: u128 = NumberFormatBuilder::new() .no_integer_leading_zeros(true) .no_float_leading_zeros(true) .internal_digit_separator(true) - .build(); - -const_assert!(NumberFormat::<{ TOML }> {}.is_valid()); + .build_strict(); // YAML (defined in-terms of JSON schema). -/// Number format for a `YAML` literal floating-point number. +/// Number format for a [`YAML`][`YAML-REF`] literal floating-point number. +/// +/// [`YAML-REF`]: https://yaml.org/ pub const YAML: u128 = JSON; // XML [01234578MN] -/// Number format for a `XML` literal floating-point number. +/// Number format for an [`XML`][`XML-REF`] literal floating-point number. +/// +/// [`XML-REF`]: https://en.wikipedia.org/wiki/XML #[rustfmt::skip] pub const XML: u128 = NumberFormatBuilder::new() .required_exponent_digits(false) .case_sensitive_special(true) - .build(); - -const_assert!(NumberFormat::<{ XML }> {}.is_valid()); + .build_strict(); // SQLITE [013456MN] -/// Number format for a `SQLite` literal floating-point number. +/// Number format for a [`SQLite`] literal floating-point number. +/// +/// [`SQLite`]: https://www.sqlite.org/ #[rustfmt::skip] pub const SQLITE: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ SQLITE }> {}.is_valid()); + .build_strict(); // POSTGRESQL [013456MN] -/// Number format for a `PostgreSQL` literal floating-point number. +/// Number format for a [`PostgreSQL`] literal floating-point number. +/// +/// [`PostgreSQL`]: https://www.postgresql.org/ #[rustfmt::skip] pub const POSTGRESQL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ POSTGRESQL }> {}.is_valid()); + .build_strict(); // MYSQL [013456MN] -/// Number format for a `MySQL` literal floating-point number. +/// Number format for a [`MySQL`] literal floating-point number. +/// +/// [`MySQL`]: https://www.mysql.com/ #[rustfmt::skip] pub const MYSQL: u128 = NumberFormatBuilder::new() .no_special(true) - .build(); - -const_assert!(NumberFormat::<{ MYSQL }> {}.is_valid()); + .build_strict(); // MONGODB [01345678M] -/// Number format for a `MongoDB` literal floating-point number. +/// Number format for a [`MongoDB`] literal floating-point number. +/// +/// [`MongoDB`]: https://www.mongodb.com/ #[rustfmt::skip] pub const MONGODB: u128 = NumberFormatBuilder::new() .case_sensitive_special(true) .no_float_leading_zeros(true) - .build(); - -const_assert!(NumberFormat::<{ MONGODB }> {}.is_valid()); + .build_strict(); // HIDDEN DEFAULTS AND INTERFACES @@ -2800,9 +3575,7 @@ const_assert!(NumberFormat::<{ MONGODB }> {}.is_valid()); pub const PERMISSIVE: u128 = NumberFormatBuilder::new() .required_exponent_digits(false) .required_mantissa_digits(false) - .build(); - -const_assert!(NumberFormat::<{ PERMISSIVE }> {}.is_valid()); + .build_strict(); /// Number format when all digit separator flags are set. #[doc(hidden)] @@ -2812,6 +3585,4 @@ pub const IGNORE: u128 = NumberFormatBuilder::new() .digit_separator_flags(true) .required_exponent_digits(false) .required_mantissa_digits(false) - .build(); - -const_assert!(NumberFormat::<{ IGNORE }> {}.is_valid()); + .build_strict(); diff --git a/lexical-util/src/format.rs b/lexical-util/src/format.rs index e922fa92..4f1d54e8 100644 --- a/lexical-util/src/format.rs +++ b/lexical-util/src/format.rs @@ -1,251 +1,629 @@ -//! Public API for the number format packed struct. +//! The creation and processing of number format packed structs. //! -//! This has a consistent API whether or not the `format` feature is -//! enabled, however, most functionality will be disabled if the feature -//! is not enabled. +//! This creates the format specification as a 128-bit packed struct, +//! represented as a [`u128`] through the [`NumberFormatBuilder`] and +//! with helpers to access format options through [`NumberFormat`]. +//! +//! This has a consistent API whether or not the [`format`][crate#features] +//! feature is enabled, however, most functionality will be disabled if the +//! feature is not enabled. +//! +//! # Creating Formats +//! +//! Formats can be created through [`NumberFormatBuilder`]: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! use core::num; +//! +//! use lexical_util::{NumberFormat, NumberFormatBuilder}; +//! +//! // create the format for literal Rustt floats +//! const RUST: u128 = NumberFormatBuilder::new() +//! .digit_separator(num::NonZeroU8::new(b'_')) +//! .required_digits(true) +//! .no_positive_mantissa_sign(true) +//! .no_special(true) +//! .internal_digit_separator(true) +//! .trailing_digit_separator(true) +//! .consecutive_digit_separator(true) +//! .build_strict(); +//! +//! // then, access the formats's properties +//! let format = NumberFormat::<{ RUST }> {}; +//! assert!(format.no_positive_mantissa_sign()); +//! assert!(format.no_special()); +//! assert!(format.internal_digit_separator()); +//! assert!(format.trailing_digit_separator()); +//! assert!(format.consecutive_digit_separator()); +//! assert!(!format.no_exponent_notation()); +//! # } +//! ``` +//! +//! These pre-built formats can then be used for [`FromLexicalWithOptions`] +//! and [`ToLexicalWithOptions`] conversions. //! //! # Pre-Defined Formats //! //! These are the pre-defined formats for parsing numbers from various //! programming, markup, and data languages. //! -//! - [STANDARD] -#![cfg_attr(feature = "format", doc = " - [`RUST_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`RUST_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON3_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON3_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON36_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON35_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON2_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PYTHON2_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX20_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX20_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX20_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX20_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX17_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX17_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX17_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX17_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX14_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX14_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX14_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX11_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX11_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`CXX11_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX03_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX03_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX98_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CXX98_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C18_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C18_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C18_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C18_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C11_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C11_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C11_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C11_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C99_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C99_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C99_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C99_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C90_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C90_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C90_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`C89_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`C89_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`C89_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`RUBY_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`RUBY_OCTAL_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`RUBY_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`SWIFT_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`SWIFT_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`GO_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`GO_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`HASKELL_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`HASKELL_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`JAVASCRIPT_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`JAVASCRIPT_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`PERL_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PERL_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`PHP_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`PHP_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`JAVA_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`JAVA_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`R_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`R_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`KOTLIN_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`KOTLIN_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`JULIA_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`JULIA_STRING`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`JULIA_HEX_LITERAL`]")] -#![cfg_attr(all(feature = "format", feature = "power-of-two"), doc = " - [`JULIA_HEX_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP7_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP7_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP6_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP6_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP5_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP5_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP4_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP4_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP3_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP3_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP2_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP2_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP1_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CSHARP1_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`KAWA_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`KAWA_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`GAMBITC_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`GAMBITC_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`GUILE_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`GUILE_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`CLOJURE_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`CLOJURE_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`ERLANG_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`ERLANG_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`ELM_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`ELM_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`SCALA_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`SCALA_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`ELIXIR_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`ELIXIR_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`FORTRAN_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`FORTRAN_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`D_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`D_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`COFFEESCRIPT_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`COFFEESCRIPT_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`COBOL_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`COBOL_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`FSHARP_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`FSHARP_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`VB_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`VB_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`OCAML_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`OCAML_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`OBJECTIVEC_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`OBJECTIVEC_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`REASONML_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`REASONML_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`OCTAVE_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`OCTAVE_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`MATLAB_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`MATLAB_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`ZIG_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`ZIG_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`SAGE_LITERAL`]")] -#![cfg_attr(feature = "format", doc = " - [`SAGE_STRING`]")] -#![cfg_attr(feature = "format", doc = " - [`JSON`]")] -#![cfg_attr(feature = "format", doc = " - [`TOML`]")] -#![cfg_attr(feature = "format", doc = " - [`YAML`]")] -#![cfg_attr(feature = "format", doc = " - [`XML`]")] -#![cfg_attr(feature = "format", doc = " - [`SQLITE`]")] -#![cfg_attr(feature = "format", doc = " - [`POSTGRESQL`]")] -#![cfg_attr(feature = "format", doc = " - [`MYSQL`]")] -#![cfg_attr(feature = "format", doc = " - [`MONGODB`]")] -//! -//! # Syntax Flags +//! - [`STANDARD`]: Standard number format. This is identical to the Rust string +//! format. +#![cfg_attr( + feature = "format", + doc = " +- [`RUST_LITERAL`]: Number format for a [`Rust`] literal floating-point number. +- [`RUST_STRING`]: Number format to parse a [`Rust`] float from string. +- [`PYTHON_LITERAL`]: Number format for a [`Python`] literal floating-point number. +- [`PYTHON_STRING`]: Number format to parse a [`Python`] float from string. +- [`PYTHON3_LITERAL`]: Number format for a [`Python3`] literal floating-point number. +- [`PYTHON3_STRING`]: Number format to parse a [`Python3`] float from string. +- [`PYTHON36_LITERAL`]: Number format for a [`Python3.6`] or higher literal floating-point number. +- [`PYTHON35_LITERAL`]: Number format for a [`Python3.5`] or lower literal floating-point number. +- [`PYTHON2_LITERAL`]: Number format for a [`Python2`] literal floating-point number. +- [`PYTHON2_STRING`]: Number format to parse a [`Python2`] float from string. +- [`CXX_LITERAL`]: Number format for a [`C++`] literal floating-point number. +- [`CXX_STRING`]: Number format to parse a [`C++`] float from string. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX_HEX_LITERAL`]: Number format for a [`C++`] literal hexadecimal floating-point number. +- [`CXX_HEX_STRING`]: Number format to parse a [`C++`] hexadecimal float from string. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX20_LITERAL`]: Number format for a [`C++20`] literal floating-point number. +- [`CXX20_STRING`]: Number format for a [`C++20`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX20_HEX_LITERAL`]: Number format for a [`C++20`] literal hexadecimal floating-point number. +- [`CXX20_HEX_STRING`]: Number format for a [`C++20`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX17_LITERAL`]: Number format for a [`C++17`] literal floating-point number. +- [`CXX17_STRING`]: Number format for a [`C++17`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX17_HEX_LITERAL`]: Number format for a [`C++17`] literal hexadecimal floating-point number. +- [`CXX17_HEX_STRING`]: Number format for a [`C++17`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX14_LITERAL`]: Number format for a [`C++14`] literal floating-point number. +- [`CXX14_STRING`]: Number format for a [`C++14`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX14_HEX_STRING`]: Number format for a [`C++14`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX11_LITERAL`]: Number format for a [`C++11`] literal floating-point number. +- [`CXX11_STRING`]: Number format for a [`C++11`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`CXX11_HEX_STRING`]: Number format for a [`C++11`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CXX03_LITERAL`]: Number format for a [`C++03`] literal floating-point number. +- [`CXX03_STRING`]: Number format for a [`C++03`] string floating-point number. +- [`CXX98_LITERAL`]: Number format for a [`C++98`] literal floating-point number. +- [`CXX98_STRING`]: Number format for a [`C++98`] string floating-point number. +- [`C_LITERAL`]: Number format for a [`C`] literal floating-point number. +- [`C_STRING`]: Number format for a [`C`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C_HEX_LITERAL`]: Number format for a [`C`] literal hexadecimal floating-point number. +- [`C_HEX_STRING`]: Number format for a [`C`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C18_LITERAL`]: Number format for a [`C18`] literal floating-point number. +- [`C18_STRING`]: Number format for a [`C18`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C18_HEX_LITERAL`]: Number format for a [`C18`] literal hexadecimal floating-point number. +- [`C18_HEX_STRING`]: Number format for a [`C18`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C11_LITERAL`]: Number format for a [`C11`] literal floating-point number. +- [`C11_STRING`]: Number format for a [`C11`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C11_HEX_LITERAL`]: Number format for a [`C11`] literal hexadecimal floating-point number. +- [`C11_HEX_STRING`]: Number format for a [`C11`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C99_LITERAL`]: Number format for a [`C99`] literal floating-point number. +- [`C99_STRING`]: Number format for a [`C99`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C99_HEX_LITERAL`]: Number format for a [`C99`] literal hexadecimal floating-point number. +- [`C99_HEX_STRING`]: Number format for a [`C99`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C90_LITERAL`]: Number format for a [`C90`] literal floating-point number. +- [`C90_STRING`]: Number format for a [`C90`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C90_HEX_STRING`]: Number format for a [`C90`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`C89_LITERAL`]: Number format for a [`C89`] literal floating-point number. +- [`C89_STRING`]: Number format for a [`C89`] string floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`C89_HEX_STRING`]: Number format for a [`C89`] string hexadecimal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`RUBY_LITERAL`]: Number format for a [`Ruby`] literal floating-point number. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`RUBY_OCTAL_LITERAL`]: Number format for an octal [`Ruby`] literal floating-point number. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`RUBY_STRING`]: Number format to parse a [`Ruby`] float from string. +- [`SWIFT_LITERAL`]: Number format for a [`Swift`] literal floating-point number. +- [`SWIFT_STRING`]: Number format to parse a [`Swift`] float from string. +- [`GO_LITERAL`]: Number format for a [`Golang`] literal floating-point number. +- [`GO_STRING`]: Number format to parse a [`Golang`] float from string. +- [`HASKELL_LITERAL`]: Number format for a [`Haskell`] literal floating-point number. +- [`HASKELL_STRING`]: Number format to parse a [`Haskell`] float from string. +- [`JAVASCRIPT_LITERAL`]: Number format for a [`Javascript`] literal floating-point number. +- [`JAVASCRIPT_STRING`]: Number format to parse a [`Javascript`] float from string. +- [`PERL_LITERAL`]: Number format for a [`Perl`] literal floating-point number. +- [`PERL_STRING`]: Number format to parse a [`Perl`] float from string. +- [`PHP_LITERAL`]: Number format for a [`PHP`] literal floating-point number. +- [`PHP_STRING`]: Number format to parse a [`PHP`] float from string. +- [`JAVA_LITERAL`]: Number format for a [`Java`] literal floating-point number. +- [`JAVA_STRING`]: Number format to parse a [`Java`] float from string. +- [`R_LITERAL`]: Number format for an [`R`] literal floating-point number. +- [`R_STRING`]: Number format to parse an [`R`] float from string. +- [`KOTLIN_LITERAL`]: Number format for a [`Kotlin`] literal floating-point number. +- [`KOTLIN_STRING`]: Number format to parse a [`Kotlin`] float from string. +- [`JULIA_LITERAL`]: Number format for a [`Julia`] literal floating-point number. +- [`JULIA_STRING`]: Number format to parse a [`Julia`] float from string. +" +)] +#![cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = " +- [`JULIA_HEX_LITERAL`]: Number format for a [`Julia`] literal floating-point number. +- [`JULIA_HEX_STRING`]: Number format to parse a [`Julia`] float from string. +" +)] +#![cfg_attr( + feature = "format", + doc = " +- [`CSHARP_LITERAL`]: Number format for a [`C#`] literal floating-point number. +- [`CSHARP_STRING`]: Number format to parse a [`C#`] float from string. +- [`CSHARP7_LITERAL`]: Number format for a [`C#7`] literal floating-point number. +- [`CSHARP7_STRING`]: Number format to parse a [`C#7`] float from string. +- [`CSHARP6_LITERAL`]: Number format for a [`C#6`] literal floating-point number. +- [`CSHARP6_STRING`]: Number format to parse a [`C#6`] float from string. +- [`CSHARP5_LITERAL`]: Number format for a [`C#5`] literal floating-point number. +- [`CSHARP5_STRING`]: Number format to parse a [`C#5`] float from string. +- [`CSHARP4_LITERAL`]: Number format for a [`C#4`] literal floating-point number. +- [`CSHARP4_STRING`]: Number format to parse a [`C#4`] float from string. +- [`CSHARP3_LITERAL`]: Number format for a [`C#3`] literal floating-point number. +- [`CSHARP3_STRING`]: Number format to parse a [`C#3`] float from string. +- [`CSHARP2_LITERAL`]: Number format for a [`C#2`] literal floating-point number. +- [`CSHARP2_STRING`]: Number format to parse a [`C#2`] float from string. +- [`CSHARP1_LITERAL`]: Number format for a [`C#1`] literal floating-point number. +- [`CSHARP1_STRING`]: Number format to parse a [`C#1`] float from string. +- [`KAWA_LITERAL`]: Number format for a [`Kawa`] literal floating-point number. +- [`KAWA_STRING`]: Number format to parse a [`Kawa`] float from string. +- [`GAMBITC_LITERAL`]: Number format for a [`Gambit-C`] literal floating-point number. +- [`GAMBITC_STRING`]: Number format to parse a [`Gambit-C`] float from string. +- [`GUILE_LITERAL`]: Number format for a [`Guile`] literal floating-point number. +- [`GUILE_STRING`]: Number format to parse a [`Guile`] float from string. +- [`CLOJURE_LITERAL`]: Number format for a [`Clojure`] literal floating-point number. +- [`CLOJURE_STRING`]: Number format to parse a [`Clojure`] float from string. +- [`ERLANG_LITERAL`]: Number format for an [`Erlang`] literal floating-point number. +- [`ERLANG_STRING`]: Number format to parse an [`Erlang`] float from string. +- [`ELM_LITERAL`]: Number format for an [`Elm`] literal floating-point number. +- [`ELM_STRING`]: Number format to parse an [`Elm`] float from string. +- [`SCALA_LITERAL`]: Number format for a [`Scala`] literal floating-point number. +- [`SCALA_STRING`]: Number format to parse a [`Scala`] float from string. +- [`ELIXIR_LITERAL`]: Number format for an [`Elixir`] literal floating-point number. +- [`ELIXIR_STRING`]: Number format to parse an [`Elixir`] float from string. +- [`FORTRAN_LITERAL`]: Number format for a [`FORTRAN`] literal floating-point number. +- [`FORTRAN_STRING`]: Number format to parse a [`FORTRAN`] float from string. +- [`D_LITERAL`]: Number format for a [`D`] literal floating-point number. +- [`D_STRING`]: Number format to parse a [`D`] float from string. +- [`COFFEESCRIPT_LITERAL`]: Number format for a [`Coffeescript`] literal floating-point number. +- [`COFFEESCRIPT_STRING`]: Number format to parse a [`Coffeescript`] float from string. +- [`COBOL_LITERAL`]: Number format for a [`Cobol`] literal floating-point number. +- [`COBOL_STRING`]: Number format to parse a [`Cobol`] float from string. +- [`FSHARP_LITERAL`]: Number format for a [`F#`] literal floating-point number. +- [`FSHARP_STRING`]: Number format to parse a [`F#`] float from string. +- [`VB_LITERAL`]: Number format for a [`Visual Basic`] literal floating-point number. +- [`VB_STRING`]: Number format to parse a [`Visual Basic`] float from string. +- [`OCAML_LITERAL`]: Number format for an [`OCaml`] literal floating-point number. +- [`OCAML_STRING`]: Number format to parse an [`OCaml`] float from string. +- [`OBJECTIVEC_LITERAL`]: Number format for an [`Objective-C`] literal floating-point number. +- [`OBJECTIVEC_STRING`]: Number format to parse an [`Objective-C`] float from string. +- [`REASONML_LITERAL`]: Number format for a [`ReasonML`] literal floating-point number. +- [`REASONML_STRING`]: Number format to parse a [`ReasonML`] float from string. +- [`OCTAVE_LITERAL`]: Number format for an [`Octave`] literal floating-point number. +- [`OCTAVE_STRING`]: Number format to parse an [`Octave`] float from string. +- [`MATLAB_LITERAL`]: Number format for an [`Matlab`] literal floating-point number. +- [`MATLAB_STRING`]: Number format to parse an [`Matlab`] float from string. +- [`ZIG_LITERAL`]: Number format for a [`Zig`] literal floating-point number. +- [`ZIG_STRING`]: Number format to parse a [`Zig`] float from string. +- [`SAGE_LITERAL`]: Number format for a [`Sage`] literal floating-point number. +- [`SAGE_STRING`]: Number format to parse a [`Sage`] float from string. +- [`JSON`]: Number format for a [`JSON`][`JSON-REF`] literal floating-point number. +- [`TOML`]: Number format for a [`TOML`][`TOML-REF`] literal floating-point number. +- [`YAML`]: Number format for a [`YAML`][`YAML-REF`] literal floating-point number. +- [`XML`]: Number format for an [`XML`][`XML-REF`] literal floating-point number. +- [`SQLITE`]: Number format for a [`SQLite`] literal floating-point number. +- [`POSTGRESQL`]: Number format for a [`PostgreSQL`] literal floating-point number. +- [`MYSQL`]: Number format for a [`MySQL`] literal floating-point number. +- [`MONGODB`]: Number format for a [`MongoDB`] literal floating-point number. +" +)] +//! +#![cfg_attr( + feature = "parse", + doc = "[`FromLexicalWithOptions`]: crate::from_lexical_with_options" +)] +#![cfg_attr( + not(feature = "parse"), + doc = "[`FromLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L45" +)] +#![cfg_attr(feature = "write", doc = "[`ToLexicalWithOptions`]: crate::to_lexical_with_options")] +#![cfg_attr( + not(feature = "write"), + doc = "[`ToLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L151" +)] +//! +//! # Low-Level Schema +//! +//! This describes how to directly get and set flags from the [`NumberFormat`] +//! packed struct. It is not recommended to use these directly, but for example, +//! the following can be done: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! use lexical_util::format; +//! +//! assert_eq!( +//! format::NumberFormatBuilder::new() +//! .required_integer_digits(true) +//! .build_strict(), +//! format::STANDARD | format::REQUIRED_INTEGER_DIGITS +//! ); +//! # } +//! ``` +//! +//! ## Syntax Flags //! //! Bitflags to get and set syntax flags for the format packed struct. //! -//! - [`REQUIRED_INTEGER_DIGITS`] -//! - [`REQUIRED_FRACTION_DIGITS`] -//! - [`REQUIRED_EXPONENT_DIGITS`] -//! - [`REQUIRED_MANTISSA_DIGITS`] -//! - [`REQUIRED_DIGITS`] -//! - [`NO_POSITIVE_MANTISSA_SIGN`] -//! - [`REQUIRED_MANTISSA_SIGN`] -//! - [`NO_EXPONENT_NOTATION`] -//! - [`NO_POSITIVE_EXPONENT_SIGN`] -//! - [`REQUIRED_EXPONENT_SIGN`] -//! - [`NO_EXPONENT_WITHOUT_FRACTION`] -//! - [`NO_SPECIAL`] -//! - [`CASE_SENSITIVE_SPECIAL`] -//! - [`NO_INTEGER_LEADING_ZEROS`] -//! - [`NO_FLOAT_LEADING_ZEROS`] -//! - [`REQUIRED_EXPONENT_NOTATION`] -//! - [`CASE_SENSITIVE_EXPONENT`] -//! - [`CASE_SENSITIVE_BASE_PREFIX`] -//! - [`CASE_SENSITIVE_BASE_SUFFIX`] -//! -//! # Digit Separator Flags +//! - [`REQUIRED_INTEGER_DIGITS`]: If digits are required before the decimal +//! point. +//! - [`REQUIRED_FRACTION_DIGITS`]: If digits are required after the decimal +//! point. +//! - [`REQUIRED_EXPONENT_DIGITS`]: If digits are required after the exponent +//! character. +//! - [`REQUIRED_MANTISSA_DIGITS`]: If significant digits are required. +//! - [`REQUIRED_DIGITS`]: If at least 1 digit in the number is required. +//! - [`NO_POSITIVE_MANTISSA_SIGN`]: If a positive sign before the mantissa is +//! not allowed. +//! - [`REQUIRED_MANTISSA_SIGN`]: If a sign symbol before the mantissa is +//! required. +//! - [`NO_EXPONENT_NOTATION`]: If exponent notation is not allowed. +//! - [`NO_POSITIVE_EXPONENT_SIGN`]: If a positive sign before the exponent is +//! not allowed. +//! - [`REQUIRED_EXPONENT_SIGN`]: If a sign symbol before the exponent is +//! required. +//! - [`NO_EXPONENT_WITHOUT_FRACTION`]: If an exponent without fraction is not +//! allowed. +//! - [`NO_SPECIAL`]: If special (non-finite) values are not allowed. +//! - [`CASE_SENSITIVE_SPECIAL`]: If special (non-finite) values are +//! case-sensitive. +//! - [`NO_INTEGER_LEADING_ZEROS`]: If leading zeros before an integer are not +//! allowed. +//! - [`NO_FLOAT_LEADING_ZEROS`]: If leading zeros before a float are not +//! allowed. +//! - [`REQUIRED_EXPONENT_NOTATION`]: If exponent notation is required. +//! - [`CASE_SENSITIVE_EXPONENT`]: If exponent characters are case-sensitive. +//! - [`CASE_SENSITIVE_BASE_PREFIX`]: If base prefixes are case-sensitive. +//! - [`CASE_SENSITIVE_BASE_SUFFIX`]: If base suffixes are case-sensitive. +//! +//! [`REQUIRED_INTEGER_DIGITS`]: NumberFormat::REQUIRED_INTEGER_DIGITS +//! [`REQUIRED_FRACTION_DIGITS`]: NumberFormat::REQUIRED_FRACTION_DIGITS +//! [`REQUIRED_EXPONENT_DIGITS`]: NumberFormat::REQUIRED_EXPONENT_DIGITS +//! [`REQUIRED_MANTISSA_DIGITS`]: NumberFormat::REQUIRED_MANTISSA_DIGITS +//! [`REQUIRED_DIGITS`]: NumberFormat::REQUIRED_DIGITS +//! [`NO_POSITIVE_MANTISSA_SIGN`]: NumberFormat::NO_POSITIVE_MANTISSA_SIGN +//! [`REQUIRED_MANTISSA_SIGN`]: NumberFormat::REQUIRED_MANTISSA_SIGN +//! [`NO_EXPONENT_NOTATION`]: NumberFormat::NO_EXPONENT_NOTATION +//! [`NO_POSITIVE_EXPONENT_SIGN`]: NumberFormat::NO_POSITIVE_EXPONENT_SIGN +//! [`REQUIRED_EXPONENT_SIGN`]: NumberFormat::REQUIRED_EXPONENT_SIGN +//! [`NO_EXPONENT_WITHOUT_FRACTION`]: NumberFormat::NO_EXPONENT_WITHOUT_FRACTION +//! [`NO_SPECIAL`]: NumberFormat::NO_SPECIAL +//! [`CASE_SENSITIVE_SPECIAL`]: NumberFormat::CASE_SENSITIVE_SPECIAL +//! [`NO_INTEGER_LEADING_ZEROS`]: NumberFormat::NO_INTEGER_LEADING_ZEROS +//! [`NO_FLOAT_LEADING_ZEROS`]: NumberFormat::NO_FLOAT_LEADING_ZEROS +//! [`REQUIRED_EXPONENT_NOTATION`]: NumberFormat::REQUIRED_EXPONENT_NOTATION +//! [`CASE_SENSITIVE_EXPONENT`]: NumberFormat::CASE_SENSITIVE_EXPONENT +//! [`CASE_SENSITIVE_BASE_PREFIX`]: NumberFormat::CASE_SENSITIVE_BASE_PREFIX +//! [`CASE_SENSITIVE_BASE_SUFFIX`]: NumberFormat::CASE_SENSITIVE_BASE_SUFFIX +//! +//! ## Digit Separator Flags //! //! Bitflags to get and set digit separators flags for the format //! packed struct. //! -//! - [`INTEGER_INTERNAL_DIGIT_SEPARATOR`] -//! - [`FRACTION_INTERNAL_DIGIT_SEPARATOR`] -//! - [`EXPONENT_INTERNAL_DIGIT_SEPARATOR`] -//! - [`INTEGER_LEADING_DIGIT_SEPARATOR`] -//! - [`FRACTION_LEADING_DIGIT_SEPARATOR`] -//! - [`EXPONENT_LEADING_DIGIT_SEPARATOR`] -//! - [`INTEGER_TRAILING_DIGIT_SEPARATOR`] -//! - [`FRACTION_TRAILING_DIGIT_SEPARATOR`] -//! - [`EXPONENT_TRAILING_DIGIT_SEPARATOR`] -//! - [`INTEGER_CONSECUTIVE_DIGIT_SEPARATOR`] -//! - [`FRACTION_CONSECUTIVE_DIGIT_SEPARATOR`] -//! - [`EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR`] -//! - [`INTERNAL_DIGIT_SEPARATOR`] -//! - [`LEADING_DIGIT_SEPARATOR`] -//! - [`TRAILING_DIGIT_SEPARATOR`] -//! - [`CONSECUTIVE_DIGIT_SEPARATOR`] -//! - [`SPECIAL_DIGIT_SEPARATOR`] -//! -//! # Character Shifts and Masks +//! - [`INTEGER_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between integer digits. +//! - [`FRACTION_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between fraction digits. +//! - [`EXPONENT_INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed +//! between exponent digits. +//! - [`INTEGER_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any integer digits. +//! - [`FRACTION_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any integer digits. +//! - [`EXPONENT_LEADING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! before any exponent digits. +//! - [`INTEGER_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any integer digits. +//! - [`FRACTION_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any fraction digits. +//! - [`EXPONENT_TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed +//! after any exponent digits. +//! - [`INTEGER_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive integer +//! digit separators are allowed. +//! - [`FRACTION_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive fraction +//! digit separators are allowed. +//! - [`EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive exponent +//! digit separators are allowed. +//! - [`INTERNAL_DIGIT_SEPARATOR`]: If digit separators are allowed between +//! digits. +//! - [`LEADING_DIGIT_SEPARATOR`]: Get if a digit separator is allowed before +//! any digits. +//! - [`TRAILING_DIGIT_SEPARATOR`]: If a digit separator is allowed after any +//! digits. +//! - [`CONSECUTIVE_DIGIT_SEPARATOR`]: If multiple consecutive digit separators +//! are allowed. +//! - [`SPECIAL_DIGIT_SEPARATOR`]: If any digit separators are allowed in +//! special (non-finite) values. +//! +//! [`INTEGER_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_INTERNAL_DIGIT_SEPARATOR +//! [`FRACTION_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_INTERNAL_DIGIT_SEPARATOR +//! [`EXPONENT_INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_INTERNAL_DIGIT_SEPARATOR +//! [`INTEGER_LEADING_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_LEADING_DIGIT_SEPARATOR +//! [`FRACTION_LEADING_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_LEADING_DIGIT_SEPARATOR +//! [`EXPONENT_LEADING_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_LEADING_DIGIT_SEPARATOR +//! [`INTEGER_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_TRAILING_DIGIT_SEPARATOR +//! [`FRACTION_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_TRAILING_DIGIT_SEPARATOR +//! [`EXPONENT_TRAILING_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_TRAILING_DIGIT_SEPARATOR +//! [`INTEGER_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR +//! [`FRACTION_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR +//! [`EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR +//! [`INTERNAL_DIGIT_SEPARATOR`]: NumberFormat::INTERNAL_DIGIT_SEPARATOR +//! [`LEADING_DIGIT_SEPARATOR`]: NumberFormat::LEADING_DIGIT_SEPARATOR +//! [`TRAILING_DIGIT_SEPARATOR`]: NumberFormat::TRAILING_DIGIT_SEPARATOR +//! [`CONSECUTIVE_DIGIT_SEPARATOR`]: NumberFormat::CONSECUTIVE_DIGIT_SEPARATOR +//! [`SPECIAL_DIGIT_SEPARATOR`]: NumberFormat::SPECIAL_DIGIT_SEPARATOR +//! +//! ## Character Shifts and Masks //! //! Bitmasks and bit shifts to get and set control characters for the format //! packed struct. //! -//! - [`DIGIT_SEPARATOR_SHIFT`] -//! - [`DIGIT_SEPARATOR`] -//! - [`BASE_PREFIX_SHIFT`] -//! - [`BASE_PREFIX`] -//! - [`BASE_SUFFIX_SHIFT`] -//! - [`BASE_SUFFIX`] -//! - [`MANTISSA_RADIX_SHIFT`] -//! - [`MANTISSA_RADIX`] -//! - [`RADIX_SHIFT`] -//! - [`RADIX`] -//! - [`EXPONENT_BASE_SHIFT`] -//! - [`EXPONENT_BASE`] -//! - [`EXPONENT_RADIX_SHIFT`] -//! - [`EXPONENT_RADIX`] -//! -//! # Character Functions +//! - [`DIGIT_SEPARATOR_SHIFT`]: Shift to convert to and from a digit separator +//! as a `u8`. +//! - [`DIGIT_SEPARATOR`]: Mask to extract the digit separator character. +//! - [`BASE_PREFIX_SHIFT`]: Shift to convert to and from a base prefix as a +//! `u8`. +//! - [`BASE_PREFIX`]: Mask to extract the base prefix character. +//! - [`BASE_SUFFIX_SHIFT`]: Shift to convert to and from a base suffix as a +//! `u8`. +//! - [`BASE_SUFFIX`]: Mask to extract the base suffix character. +//! - [`MANTISSA_RADIX_SHIFT`]: Shift to convert to and from a mantissa radix as +//! a `u32`. +//! - [`MANTISSA_RADIX`]: Mask to extract the mantissa radix: the radix for the +//! significant digits. +//! - [`RADIX_SHIFT`]: Alias for [`MANTISSA_RADIX_SHIFT`]. +//! - [`RADIX`]: Alias for [`MANTISSA_RADIX`]. +//! - [`EXPONENT_BASE_SHIFT`]: Shift to convert to and from an exponent base as +//! a `u32`. +//! - [`EXPONENT_BASE`]: Mask to extract the exponent base: the base the +//! exponent is raised to. +//! - [`EXPONENT_RADIX_SHIFT`]: Shift to convert to and from an exponent radix +//! as a `u32`. +//! - [`EXPONENT_RADIX`]: Mask to extract the exponent radix: the radix for the +//! exponent digits. +//! +//! [`DIGIT_SEPARATOR_SHIFT`]: DIGIT_SEPARATOR_SHIFT +//! [`DIGIT_SEPARATOR`]: NumberFormat::DIGIT_SEPARATOR +//! [`BASE_PREFIX_SHIFT`]: BASE_PREFIX_SHIFT +//! [`BASE_PREFIX`]: NumberFormat::BASE_PREFIX +//! [`BASE_SUFFIX_SHIFT`]: BASE_SUFFIX_SHIFT +//! [`BASE_SUFFIX`]: NumberFormat::BASE_SUFFIX +//! [`MANTISSA_RADIX_SHIFT`]: MANTISSA_RADIX_SHIFT +//! [`MANTISSA_RADIX`]: NumberFormat::MANTISSA_RADIX +//! [`RADIX_SHIFT`]: RADIX_SHIFT +//! [`RADIX`]: NumberFormat::RADIX +//! [`EXPONENT_BASE_SHIFT`]: EXPONENT_BASE_SHIFT +//! [`EXPONENT_BASE`]: NumberFormat::EXPONENT_BASE +//! [`EXPONENT_RADIX_SHIFT`]: EXPONENT_RADIX_SHIFT +//! [`EXPONENT_RADIX`]: crate::NumberFormat::EXPONENT_RADIX +//! +//! ## Character Functions //! //! Functions to get control characters from the format packed struct. //! -//! - [`digit_separator`] -//! - [`base_prefix`] -//! - [`base_suffix`] -//! - [`mantissa_radix`] -//! - [`exponent_base`] -//! - [`exponent_radix`] -//! - [`radix_from_flags`] +//! - [`digit_separator`]: Extract the digit separator from the format packed +//! struct. +//! - [`base_prefix`]: Extract the base prefix character from the format packed +//! struct. +//! - [`base_suffix`]: Extract the base suffix character from the format packed +//! struct. +//! - [`mantissa_radix`]: Extract the mantissa radix from the format packed +//! struct. +//! - [`exponent_base`]: Extract the exponent base from the format packed +//! struct. +//! - [`exponent_radix`]: Extract the exponent radix from the format packed +//! struct. //! -//! # Validators +//! ## Validators //! //! Functions to validate control characters for the format packed struct. //! -//! - [`is_valid_digit_separator`] -//! - [`is_valid_base_prefix`] -//! - [`is_valid_base_suffix`] -//! - [`is_valid_punctuation`] -//! - [`is_valid_radix`] - -use static_assertions::const_assert; +//! - [`is_valid_exponent_flags`]: Determine if the provided exponent flags are +//! valid. +//! - [`is_valid_digit_separator`]: Determine if the digit separator is valid. +//! - [`is_valid_base_prefix`]: Determine if the base prefix character is valid. +//! - [`is_valid_base_suffix`]: Determine if the base suffix character is valid. +//! - [`is_valid_punctuation`]: Determine if all of the "punctuation" characters +//! are valid. +//! - [`is_valid_radix`]: Determine if the radix is valid. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`Rust`]: https://www.rust-lang.org/ +[`Python`]: https://www.python.org/ +[`Python3`]: https://www.python.org/ +[`Python3.6`]: https://www.python.org/downloads/release/python-360/ +[`Python3.5`]: https://www.python.org/downloads/release/python-350/ +[`Python2`]: https://www.python.org/downloads/release/python-270/ +[`C++`]: https://en.cppreference.com/w/ +[`C++20`]: https://en.cppreference.com/w/cpp/20 +[`C++17`]: https://en.cppreference.com/w/cpp/17 +[`C++14`]: https://en.cppreference.com/w/cpp/14 +[`C++11`]: https://en.cppreference.com/w/cpp/11 +[`C++03`]: https://en.wikipedia.org/wiki/C%2B%2B03 +[`C++98`]: https://en.cppreference.com/w/ +[`C`]: https://en.cppreference.com/w/c +[`C18`]: https://en.cppreference.com/w/c/17 +[`C11`]: https://en.cppreference.com/w/c/11 +[`C99`]: https://en.cppreference.com/w/c/99 +[`C90`]: https://en.cppreference.com/w/c +[`C89`]: https://en.cppreference.com/w/c +[`Ruby`]: https://www.ruby-lang.org/en/ +[`Swift`]: https://developer.apple.com/swift/ +[`Golang`]: https://go.dev/ +[`Haskell`]: https://www.haskell.org/ +[`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +[`Perl`]: https://www.perl.org/ +[`PHP`]: https://www.php.net/ +[`Java`]: https://www.java.com/en/ +[`R`]: https://www.r-project.org/ +[`Kotlin`]: https://kotlinlang.org/ +[`Julia`]: https://julialang.org/ +[`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +[`C#7`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-73 +[`C#6`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-60 +[`C#5`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-50 +[`C#4`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-40 +[`C#3`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-30 +[`C#2`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-20 +[`C#1`]: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12-1 +[`Kawa`]: https://www.gnu.org/software/kawa/ +[`Gambit-C`]: https://gambitscheme.org/ +[`Guile`]: https://www.gnu.org/software/guile/ +[`Clojure`]: https://clojure.org/ +[`Erlang`]: https://www.erlang.org/ +[`Elm`]: https://elm-lang.org/ +[`Scala`]: https://www.scala-lang.org/ +[`Elixir`]: https://elixir-lang.org/ +[`FORTRAN`]: https://fortran-lang.org/ +[`D`]: https://dlang.org/ +[`Coffeescript`]: https://coffeescript.org/ +[`Cobol`]: https://www.ibm.com/think/topics/cobol +[`F#`]: https://fsharp.org/ +[`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +[`OCaml`]: https://ocaml.org/ +[`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +[`ReasonML`]: https://reasonml.github.io/ +[`Octave`]: https://octave.org/ +[`Matlab`]: https://www.mathworks.com/products/matlab.html +[`Zig`]: https://ziglang.org/ +[`Sage`]: https://www.sagemath.org/ +[`JSON-REF`]: https://www.json.org/json-en.html +[`TOML-REF`]: https://toml.io/en/ +[`YAML-REF`]: https://yaml.org/ +[`XML-REF`]: https://en.wikipedia.org/wiki/XML +[`SQLite`]: https://www.sqlite.org/ +[`PostgreSQL`]: https://www.postgresql.org/ +[`MySQL`]: https://www.mysql.com/ +[`MongoDB`]: https://www.mongodb.com/ +" +)] use crate::error::Error; #[cfg(feature = "format")] @@ -271,5 +649,4 @@ pub const fn format_error() -> Error { } /// Standard number format. This is identical to the Rust string format. -pub const STANDARD: u128 = NumberFormatBuilder::new().build(); -const_assert!(NumberFormat::<{ STANDARD }> {}.is_valid()); +pub const STANDARD: u128 = NumberFormatBuilder::new().build_strict(); diff --git a/lexical-util/src/format_builder.rs b/lexical-util/src/format_builder.rs index 7ea5c09a..80078868 100644 --- a/lexical-util/src/format_builder.rs +++ b/lexical-util/src/format_builder.rs @@ -1,17 +1,16 @@ //! Builder for the number format. -use core::{mem, num}; - -use static_assertions::const_assert; +use core::num; +use crate::error::Error; use crate::format_flags as flags; +// NOTE: The size of `Option` is guaranteed to be the same. +// https://doc.rust-lang.org/std/num/type.NonZeroUsize.html /// Type with the exact same size as a `u8`. +#[doc(hidden)] pub type OptionU8 = Option; -// Ensure the sizes are identical. -const_assert!(mem::size_of::() == mem::size_of::()); - /// Add single flag to `SyntaxFormat`. macro_rules! add_flag { ($format:ident, $bool:expr, $flag:ident) => { @@ -44,81 +43,116 @@ const fn unwrap_or_zero(option: OptionU8) -> u8 { } } -/// Build number format from specifications. +/// Validating builder for [`NumberFormat`] from the provided specifications. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. +/// +/// Returns [`NumberFormat`] on calling [`build_strict`] if it was able to +/// compile the format, otherwise, returns [`None`]. +/// +/// [`NumberFormat`]: crate::NumberFormat +/// [`build_strict`]: Self::build_strict +/// +/// # Examples +/// +/// To create a format valid for Rust number literals, we can use the builder +/// API: /// -/// Returns the format on calling build if it was able to compile the format, -/// otherwise, returns None. +/// ```rust +/// # #[cfg(feature = "format")] { +/// use core::num; +/// +/// use lexical_util::{NumberFormat, NumberFormatBuilder}; +/// +/// // create the format for literal Rust floats +/// const RUST: u128 = NumberFormatBuilder::new() +/// .digit_separator(num::NonZeroU8::new(b'_')) +/// .required_digits(true) +/// .no_positive_mantissa_sign(true) +/// .no_special(true) +/// .internal_digit_separator(true) +/// .trailing_digit_separator(true) +/// .consecutive_digit_separator(true) +/// .build_strict(); +/// +/// // then, access the formats's properties +/// let format = NumberFormat::<{ RUST }> {}; +/// assert!(format.no_positive_mantissa_sign()); +/// assert!(format.no_special()); +/// assert!(format.internal_digit_separator()); +/// assert!(format.trailing_digit_separator()); +/// assert!(format.consecutive_digit_separator()); +/// assert!(!format.no_exponent_notation()); +/// # } +/// ``` /// /// # Fields /// -/// * `digit_separator` - Character to separate digits. -/// * `mantissa_radix` - Radix for mantissa digits. -/// * `exponent_base` - Base for the exponent. -/// * `exponent_radix` - Radix for the exponent digits. -/// * `base_prefix` - Optional character for the -/// base prefix. -/// * `base_suffix` - Optional character for the -/// base suffix. -/// * `required_integer_digits` - If digits are required before -/// the decimal point. -/// * `required_fraction_digits` - If digits are required after -/// the decimal point. -/// * `required_exponent_digits` - If digits are required after -/// the exponent character. -/// * `required_mantissa_digits` - If at least 1 significant -/// digit is required. -/// * `no_positive_mantissa_sign` - If positive sign before the -/// mantissa is not allowed. -/// * `required_mantissa_sign` - If positive sign before the -/// mantissa is required. -/// * `no_exponent_notation` - If exponent notation is not +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`required_integer_digits`]: If digits are required before the decimal +/// point. +/// - [`required_fraction_digits`]: If digits are required after the decimal +/// point. +/// - [`required_exponent_digits`]: If digits are required after the exponent +/// character. +/// - [`required_mantissa_digits`]: If at least 1 significant digit is required. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not /// allowed. -/// * `no_positive_exponent_sign` - If positive sign before the -/// exponent is not allowed. -/// * `required_exponent_sign` - If sign before the exponent is +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is /// required. -/// * `no_exponent_without_fraction` - If exponent without fraction -/// is not allowed. -/// * `no_special` - If special (non-finite) values -/// are not allowed. -/// * `case_sensitive_special` - If special (non-finite) values -/// are case-sensitive. -/// * `no_integer_leading_zeros` - If leading zeros before an -/// integer are not allowed. -/// * `no_float_leading_zeros` - If leading zeros before a -/// float are not allowed. -/// * `required_exponent_notation` - If exponent notation is -/// required. -/// * `case_sensitive_exponent` - If exponent characters are -/// case-sensitive. -/// * `case_sensitive_base_prefix` - If base prefixes are -/// case-sensitive. -/// * `case_sensitive_base_suffix` - If base suffixes are +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`no_exponent_without_fraction`]: If exponent without fraction is not +/// allowed. +/// - [`no_special`]: If special (non-finite) values are not allowed. +/// - [`case_sensitive_special`]: If special (non-finite) values are /// case-sensitive. -/// * `integer_internal_digit_separator` - If digit separators are -/// allowed between integer digits. -/// * `fraction_internal_digit_separator` - If digit separators are -/// allowed between fraction digits. -/// * `exponent_internal_digit_separator` - If digit separators are -/// allowed between exponent digits. -/// * `integer_leading_digit_separator` - If a digit separator is -/// allowed before any integer digits. -/// * `fraction_leading_digit_separator` - If a digit separator is -/// allowed before any fraction digits. -/// * `exponent_leading_digit_separator` - If a digit separator is -/// allowed before any exponent digits. -/// * `integer_trailing_digit_separator` - If a digit separator is -/// allowed after any integer digits. -/// * `fraction_trailing_digit_separator` - If a digit separator is -/// allowed after any fraction digits. -/// * `exponent_trailing_digit_separator` - If a digit separator is -/// allowed after any exponent digits. -/// * `integer_consecutive_digit_separator` - If multiple consecutive -/// integer digit separators are allowed. -/// * `fraction_consecutive_digit_separator` - If multiple consecutive -/// fraction digit separators are allowed. -/// * `special_digit_separator` - If any digit separators are -/// allowed in special (non-finite) values. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`no_float_leading_zeros`]: If leading zeros before a float are not +/// allowed. +/// - [`required_exponent_notation`]: If exponent notation is required. +/// - [`case_sensitive_exponent`]: If exponent characters are case-sensitive. +/// - [`case_sensitive_base_prefix`]: If base prefixes are case-sensitive. +/// - [`case_sensitive_base_suffix`]: If base suffixes are case-sensitive. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`fraction_internal_digit_separator`]: If digit separators are allowed +/// between fraction digits. +/// - [`exponent_internal_digit_separator`]: If digit separators are allowed +/// between exponent digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`fraction_leading_digit_separator`]: If a digit separator is allowed +/// before any fraction digits. +/// - [`exponent_leading_digit_separator`]: If a digit separator is allowed +/// before any exponent digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`fraction_trailing_digit_separator`]: If a digit separator is allowed +/// after any fraction digits. +/// - [`exponent_trailing_digit_separator`]: If a digit separator is allowed +/// after any exponent digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. +/// - [`fraction_consecutive_digit_separator`]: If multiple consecutive fraction +/// digit separators are allowed. +/// - [`special_digit_separator`]: If any digit separators are allowed in +/// special (non-finite) values. /// /// # Write Integer Fields /// @@ -128,71 +162,206 @@ const fn unwrap_or_zero(option: OptionU8) -> u8 { /// /// These fields are used for parsing integers: /// -/// * `digit_separator` -/// * `mantissa_radix` -/// * `base_prefix` -/// * `base_suffix` -/// * `no_positive_mantissa_sign` -/// * `required_mantissa_sign` -/// * `no_integer_leading_zeros` -/// * `integer_internal_digit_separator` -/// * `integer_leading_digit_separator` -/// * `integer_trailing_digit_separator` -/// * `integer_consecutive_digit_separator` +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. /// /// # Write Float Fields /// /// These fields are used for writing floats: /// -/// * `mantissa_radix` -/// * `exponent_base` -/// * `exponent_radix` -/// * `no_positive_mantissa_sign` -/// * `required_mantissa_sign` -/// * `no_exponent_notation` -/// * `no_positive_exponent_sign` -/// * `required_exponent_sign` -/// * `required_exponent_notation` +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`required_exponent_notation`]: If exponent notation is required. /// /// # Parse Float Fields /// /// These fields are used for parsing floats: /// -/// * `digit_separator` -/// * `mantissa_radix` -/// * `exponent_base` -/// * `exponent_radix` -/// * `base_prefix` -/// * `base_suffix` -/// * `required_integer_digits` -/// * `required_fraction_digits` -/// * `required_exponent_digits` -/// * `no_positive_mantissa_sign` -/// * `required_mantissa_sign` -/// * `no_exponent_notation` -/// * `no_positive_exponent_sign` -/// * `required_exponent_sign` -/// * `no_exponent_without_fraction` -/// * `no_special` -/// * `case_sensitive_special` -/// * `no_integer_leading_zeros` -/// * `no_float_leading_zeros` -/// * `required_exponent_notation` -/// * `case_sensitive_exponent` -/// * `case_sensitive_base_prefix` -/// * `case_sensitive_base_suffix` -/// * `integer_internal_digit_separator` -/// * `fraction_internal_digit_separator` -/// * `exponent_internal_digit_separator` -/// * `integer_leading_digit_separator` -/// * `fraction_leading_digit_separator` -/// * `exponent_leading_digit_separator` -/// * `integer_trailing_digit_separator` -/// * `fraction_trailing_digit_separator` -/// * `exponent_trailing_digit_separator` -/// * `integer_consecutive_digit_separator` -/// * `fraction_consecutive_digit_separator` -/// * `special_digit_separator` +/// - [`digit_separator`]: Character to separate digits. +/// - [`mantissa_radix`]: Radix for mantissa digits. +/// - [`exponent_base`]: Base for the exponent. +/// - [`exponent_radix`]: Radix for the exponent digits. +/// - [`base_prefix`]: Optional character for the base prefix. +/// - [`base_suffix`]: Optional character for the base suffix. +/// - [`required_mantissa_digits`]: If at least 1 significant digit is required. +/// - [`required_integer_digits`]: If digits are required before the decimal +/// point. +/// - [`required_fraction_digits`]: If digits are required after the decimal +/// point. +/// - [`required_exponent_digits`]: If digits are required after the exponent +/// character. +/// - [`no_positive_mantissa_sign`]: If positive sign before the mantissa is not +/// allowed. +/// - [`required_mantissa_sign`]: If positive sign before the mantissa is +/// required. +/// - [`no_exponent_notation`]: If exponent notation is not allowed. +/// - [`no_positive_exponent_sign`]: If positive sign before the exponent is not +/// allowed. +/// - [`required_exponent_sign`]: If sign before the exponent is required. +/// - [`no_exponent_without_fraction`]: If exponent without fraction is not +/// allowed. +/// - [`no_special`]: If special (non-finite) values are not allowed. +/// - [`case_sensitive_special`]: If special (non-finite) values are +/// case-sensitive. +/// - [`no_integer_leading_zeros`]: If leading zeros before an integer are not +/// allowed. +/// - [`no_float_leading_zeros`]: If leading zeros before a float are not +/// allowed. +/// - [`required_exponent_notation`]: If exponent notation is required. +/// - [`case_sensitive_exponent`]: If exponent characters are case-sensitive. +/// - [`case_sensitive_base_prefix`]: If base prefixes are case-sensitive. +/// - [`case_sensitive_base_suffix`]: If base suffixes are case-sensitive. +/// - [`integer_internal_digit_separator`]: If digit separators are allowed +/// between integer digits. +/// - [`fraction_internal_digit_separator`]: If digit separators are allowed +/// between fraction digits. +/// - [`exponent_internal_digit_separator`]: If digit separators are allowed +/// between exponent digits. +/// - [`integer_leading_digit_separator`]: If a digit separator is allowed +/// before any integer digits. +/// - [`fraction_leading_digit_separator`]: If a digit separator is allowed +/// before any fraction digits. +/// - [`exponent_leading_digit_separator`]: If a digit separator is allowed +/// before any exponent digits. +/// - [`integer_trailing_digit_separator`]: If a digit separator is allowed +/// after any integer digits. +/// - [`fraction_trailing_digit_separator`]: If a digit separator is allowed +/// after any fraction digits. +/// - [`exponent_trailing_digit_separator`]: If a digit separator is allowed +/// after any exponent digits. +/// - [`integer_consecutive_digit_separator`]: If multiple consecutive integer +/// digit separators are allowed. +/// - [`fraction_consecutive_digit_separator`]: If multiple consecutive fraction +/// digit separators are allowed. +/// - [`special_digit_separator`]: If any digit separators are allowed in +/// special (non-finite) values. +#[cfg_attr( + feature = "power-of-two", + doc = "\n +[`exponent_base`]: Self::exponent_base +[`exponent_radix`]: Self::exponent_radix +[`mantissa_radix`]: Self::mantissa_radix +" +)] +#[cfg_attr( + not(feature = "power-of-two"), + doc = "\n +[`exponent_base`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L602\n +[`exponent_radix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L610\n +[`mantissa_radix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L594\n +" +)] +#[cfg_attr( + feature = "format", + doc = "\n +[`digit_separator`]: Self::digit_separator\n +[`required_integer_digits`]: Self::required_integer_digits\n +[`required_fraction_digits`]: Self::required_fraction_digits\n +[`required_exponent_digits`]: Self::required_exponent_digits\n +[`required_mantissa_digits`]: Self::required_mantissa_digits\n +[`no_positive_mantissa_sign`]: Self::no_positive_mantissa_sign\n +[`required_mantissa_sign`]: Self::required_mantissa_sign\n +[`no_exponent_notation`]: Self::no_exponent_notation\n +[`no_positive_exponent_sign`]: Self::no_positive_exponent_sign\n +[`required_exponent_sign`]: Self::required_exponent_sign\n +[`no_exponent_without_fraction`]: Self::no_exponent_without_fraction\n +[`no_special`]: Self::no_special\n +[`case_sensitive_special`]: Self::case_sensitive_special\n +[`no_integer_leading_zeros`]: Self::no_integer_leading_zeros\n +[`no_float_leading_zeros`]: Self::no_float_leading_zeros\n +[`required_exponent_notation`]: Self::required_exponent_notation\n +[`case_sensitive_exponent`]: Self::case_sensitive_exponent\n +[`integer_internal_digit_separator`]: Self::integer_internal_digit_separator\n +[`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator\n +[`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator\n +[`integer_leading_digit_separator`]: Self::integer_leading_digit_separator\n +[`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator\n +[`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator\n +[`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator\n +[`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator\n +[`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator\n +[`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator\n +[`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator\n +[`special_digit_separator`]: Self::special_digit_separator\n +" +)] +#[cfg_attr( + not(feature = "format"), + doc = "\n +[`digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L579\n +[`required_integer_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L634\n +[`required_fraction_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L642\n +[`required_exponent_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L650\n +[`required_mantissa_digits`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L658\n +[`no_positive_mantissa_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L677\n +[`required_mantissa_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L685\n +[`no_exponent_notation`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L693\n +[`no_positive_exponent_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L701\n +[`required_exponent_sign`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L709\n +[`no_exponent_without_fraction`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L717\n +[`no_special`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L725\n +[`case_sensitive_special`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L733\n +[`no_integer_leading_zeros`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L741\n +[`no_float_leading_zeros`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L749\n +[`required_exponent_notation`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L757\n +[`case_sensitive_exponent`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L765\n +[`integer_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L793\n +[`fraction_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L805\n +[`exponent_internal_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L817\n +[`integer_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L842\n +[`fraction_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L853\n +[`exponent_leading_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L864\n +[`integer_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L888\n +[`fraction_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L899\n +[`exponent_trailing_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L910\n +[`integer_consecutive_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L931\n +[`fraction_consecutive_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L939\n +[`special_digit_separator`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L965\n +" +)] +#[cfg_attr( + all(feature = "format", feature = "power-of-two"), + doc = "\n +[`base_prefix`]: Self::base_prefix +[`base_suffix`]: Self::base_suffix +[`case_sensitive_base_prefix`]: Self::case_sensitive_base_prefix +[`case_sensitive_base_suffix`]: Self::case_sensitive_base_suffix +" +)] +#[cfg_attr( + not(all(feature = "format", feature = "power-of-two")), + doc = "\n +[`base_prefix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L618\n +[`base_suffix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L626\n +[`case_sensitive_base_prefix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L773\n +[`case_sensitive_base_suffix`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/format_builder.rs#L781\n +" +)] pub struct NumberFormatBuilder { digit_separator: OptionU8, base_prefix: OptionU8, @@ -236,7 +405,59 @@ pub struct NumberFormatBuilder { impl NumberFormatBuilder { // CONSTRUCTORS - /// Create new `NumberFormatBuilder` with default arguments. + /// Create new [`NumberFormatBuilder`] with default arguments. + /// + /// The default values are: + /// - [`digit_separator`][Self::get_digit_separator] - `None` + /// - [`base_prefix`][Self::get_base_prefix] - `None` + /// - [`base_suffix`][Self::get_base_suffix] - `None` + /// - [`mantissa_radix`][Self::get_mantissa_radix] - `10` + /// - [`exponent_base`][Self::get_exponent_base] - `None` + /// - [`exponent_radix`][Self::get_exponent_radix] - `None` + /// - [`required_integer_digits`][Self::get_required_integer_digits] - + /// `false` + /// - [`required_fraction_digits`][Self::get_required_fraction_digits] - + /// `false` + /// - [`required_exponent_digits`][Self::get_required_exponent_digits] - + /// `true` + /// - [`required_mantissa_digits`][Self::get_required_mantissa_digits] - + /// `true` + /// - [`no_positive_mantissa_sign`][Self::get_no_positive_mantissa_sign] - + /// `false` + /// - [`required_mantissa_sign`][Self::get_required_mantissa_sign] - `false` + /// - [`no_exponent_notation`][Self::get_no_exponent_notation] - `false` + /// - [`no_positive_exponent_sign`][Self::get_no_positive_exponent_sign] - + /// `false` + /// - [`required_exponent_sign`][Self::get_required_exponent_sign] - `false` + /// - [`no_exponent_without_fraction`][Self::get_no_exponent_without_fraction] - + /// `false` + /// - [`no_special`][Self::get_no_special] - `false` + /// - [`case_sensitive_special`][Self::get_case_sensitive_special] - `false` + /// - [`no_integer_leading_zeros`][Self::get_no_integer_leading_zeros] - + /// `false` + /// - [`no_float_leading_zeros`][Self::get_no_float_leading_zeros] - `false` + /// - [`required_exponent_notation`][Self::get_required_exponent_notation] - + /// `false` + /// - [`case_sensitive_exponent`][Self::get_case_sensitive_exponent] - + /// `false` + /// - [`case_sensitive_base_prefix`][Self::get_case_sensitive_base_prefix] - + /// `false` + /// - [`case_sensitive_base_suffix`][Self::get_case_sensitive_base_suffix] - + /// `false` + /// - [`integer_internal_digit_separator`][Self::get_integer_internal_digit_separator] - `false` + /// - [`fraction_internal_digit_separator`][Self::get_fraction_internal_digit_separator] - `false` + /// - [`exponent_internal_digit_separator`][Self::get_exponent_internal_digit_separator] - `false` + /// - [`integer_leading_digit_separator`][Self::get_integer_leading_digit_separator] - `false` + /// - [`fraction_leading_digit_separator`][Self::get_fraction_leading_digit_separator] - `false` + /// - [`exponent_leading_digit_separator`][Self::get_exponent_leading_digit_separator] - `false` + /// - [`integer_trailing_digit_separator`][Self::get_integer_trailing_digit_separator] - `false` + /// - [`fraction_trailing_digit_separator`][Self::get_fraction_trailing_digit_separator] - `false` + /// - [`exponent_trailing_digit_separator`][Self::get_exponent_trailing_digit_separator] - `false` + /// - [`integer_consecutive_digit_separator`][Self::get_integer_consecutive_digit_separator] - `false` + /// - [`fraction_consecutive_digit_separator`][Self::get_fraction_consecutive_digit_separator] - `false` + /// - [`exponent_consecutive_digit_separator`][Self::get_exponent_consecutive_digit_separator] - `false` + /// - [`special_digit_separator`][Self::get_special_digit_separator] - + /// `false` #[inline(always)] pub const fn new() -> Self { Self { @@ -282,12 +503,14 @@ impl NumberFormatBuilder { /// Create number format for standard, binary number. #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn binary() -> u128 { Self::from_radix(2) } /// Create number format for standard, octal number. #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn octal() -> u128 { Self::from_radix(8) } @@ -298,17 +521,32 @@ impl NumberFormatBuilder { builder.mantissa_radix = 10; builder.exponent_base = num::NonZeroU8::new(10); builder.exponent_radix = num::NonZeroU8::new(10); - builder.build() + builder.build_strict() } /// Create number format for standard, hexadecimal number. #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn hexadecimal() -> u128 { Self::from_radix(16) } /// Create number format from radix. + /// + ///
+ /// + /// This function will never fail even if the radix is invalid. It is up to + /// the caller to ensure the format is valid using + /// [`NumberFormat::is_valid`]. Only radixes from `2` to `36` should be + /// used. + /// + ///
+ /// + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid + // FIXME: Use `build_strict` when we can have a breaking change. + #[allow(deprecated)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn from_radix(radix: u8) -> u128 { Self::new() .radix(radix) @@ -320,144 +558,534 @@ impl NumberFormatBuilder { // GETTERS /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`None`], or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_digit_separator(&self) -> OptionU8 { self.digit_separator } /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer #[inline(always)] pub const fn get_mantissa_radix(&self) -> u8 { self.mantissa_radix } /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_exponent_base(&self) -> OptionU8 { self.exponent_base } /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`get_mantissa_radix`][Self::get_mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_exponent_radix(&self) -> OptionU8 { self.exponent_radix } /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`None`], or no base prefix allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_base_prefix(&self) -> OptionU8 { self.base_prefix } /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`None`], or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_base_suffix(&self) -> OptionU8 { self.base_suffix } /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_required_integer_digits(&self) -> bool { self.required_integer_digits } - /// Get if digits are required after the decimal point. + /// Get if digits are required after the decimal point, if the decimal point + /// is present. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_required_fraction_digits(&self) -> bool { self.required_fraction_digits } - /// Get if digits are required after the exponent character. + /// Get if digits are required after the exponent character, if the exponent + /// is present. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_required_exponent_digits(&self) -> bool { self.required_exponent_digits } /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_required_mantissa_digits(&self) -> bool { self.required_mantissa_digits } /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn get_no_positive_mantissa_sign(&self) -> bool { self.no_positive_mantissa_sign } /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn get_required_mantissa_sign(&self) -> bool { self.required_mantissa_sign } /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn get_no_exponent_notation(&self) -> bool { self.no_exponent_notation } /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn get_no_positive_exponent_sign(&self) -> bool { self.no_positive_exponent_sign } /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn get_required_exponent_sign(&self) -> bool { self.required_exponent_sign } /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_no_exponent_without_fraction(&self) -> bool { self.no_exponent_without_fraction } /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_no_special(&self) -> bool { self.no_special } /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_case_sensitive_special(&self) -> bool { self.case_sensitive_special } /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer #[inline(always)] pub const fn get_no_integer_leading_zeros(&self) -> bool { self.no_integer_leading_zeros } /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_no_float_leading_zeros(&self) -> bool { self.no_float_leading_zeros } /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn get_required_exponent_notation(&self) -> bool { self.required_exponent_notation } /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_case_sensitive_exponent(&self) -> bool { self.case_sensitive_exponent } /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_case_sensitive_base_prefix(&self) -> bool { self.case_sensitive_base_prefix } /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_case_sensitive_base_suffix(&self) -> bool { self.case_sensitive_base_suffix @@ -467,7 +1095,25 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_integer_internal_digit_separator(&self) -> bool { self.integer_internal_digit_separator @@ -477,7 +1123,24 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_fraction_internal_digit_separator(&self) -> bool { self.fraction_internal_digit_separator @@ -487,7 +1150,24 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_exponent_internal_digit_separator(&self) -> bool { self.exponent_internal_digit_separator @@ -496,7 +1176,25 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed before any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_integer_leading_digit_separator(&self) -> bool { self.integer_leading_digit_separator @@ -505,7 +1203,24 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed before any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_fraction_leading_digit_separator(&self) -> bool { self.fraction_leading_digit_separator @@ -514,7 +1229,24 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed before any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_exponent_leading_digit_separator(&self) -> bool { self.exponent_leading_digit_separator @@ -523,7 +1255,25 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed after any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_integer_trailing_digit_separator(&self) -> bool { self.integer_trailing_digit_separator @@ -532,7 +1282,22 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed after any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_fraction_trailing_digit_separator(&self) -> bool { self.fraction_trailing_digit_separator @@ -541,31 +1306,85 @@ impl NumberFormatBuilder { /// Get if a digit separator is allowed after any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_exponent_trailing_digit_separator(&self) -> bool { self.exponent_trailing_digit_separator } /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn get_integer_consecutive_digit_separator(&self) -> bool { self.integer_consecutive_digit_separator } /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_fraction_consecutive_digit_separator(&self) -> bool { self.fraction_consecutive_digit_separator } /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_exponent_consecutive_digit_separator(&self) -> bool { self.exponent_consecutive_digit_separator } /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn get_special_digit_separator(&self) -> bool { self.special_digit_separator @@ -574,95 +1393,311 @@ impl NumberFormatBuilder { // SETTERS /// Set the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Defaults to [`None`], or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn digit_separator(mut self, character: OptionU8) -> Self { self.digit_separator = character; self } - /// Alias for mantissa radix. + /// Alias for [`mantissa radix`][Self::mantissa_radix]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer #[inline(always)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn radix(self, radix: u8) -> Self { self.mantissa_radix(radix) } /// Set the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer #[inline(always)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn mantissa_radix(mut self, radix: u8) -> Self { self.mantissa_radix = radix; self } /// Set the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn exponent_base(mut self, base: OptionU8) -> Self { self.exponent_base = base; self } /// Set the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is 10 as + /// is the exponent base. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn exponent_radix(mut self, radix: OptionU8) -> Self { self.exponent_radix = radix; self } /// Set the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Defaults to [`None`], or no base prefix + /// allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn base_prefix(mut self, base_prefix: OptionU8) -> Self { self.base_prefix = base_prefix; self } /// Set the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Defaults to [`None`], or no base suffix + /// allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn base_suffix(mut self, base_suffix: OptionU8) -> Self { self.base_suffix = base_suffix; self } /// Set if digits are required before the decimal point. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_integer_digits(mut self, flag: bool) -> Self { self.required_integer_digits = flag; self } - /// Set if digits are required after the decimal point. + /// Set if digits are required after the decimal point, if the decimal point + /// is present. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_fraction_digits(mut self, flag: bool) -> Self { self.required_fraction_digits = flag; self } - /// Set if digits are required after the exponent character. + /// Set if digits are required after the exponent character, if the exponent + /// is present. + /// + /// Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_digits(mut self, flag: bool) -> Self { self.required_exponent_digits = flag; self } /// Set if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_mantissa_digits(mut self, flag: bool) -> Self { self.required_mantissa_digits = flag; self } /// Set if digits are required for all float components. + /// + /// Note that digits are **always** required for integers. Defaults + /// to requiring digits only for the mantissa and exponent. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `0.1` | ✔️ | + /// | `.1` | ❌ | + /// | `1.` | ❌ | + /// | `e10` | ❌ | + /// | `.1e10` | ❌ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_digits(mut self, flag: bool) -> Self { self = self.required_integer_digits(flag); self = self.required_fraction_digits(flag); @@ -672,112 +1707,312 @@ impl NumberFormatBuilder { } /// Set if a positive sign before the mantissa is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_positive_mantissa_sign(mut self, flag: bool) -> Self { self.no_positive_mantissa_sign = flag; self } /// Set if a sign symbol before the mantissa is required. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_mantissa_sign(mut self, flag: bool) -> Self { self.required_mantissa_sign = flag; self } /// Set if exponent notation is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_exponent_notation(mut self, flag: bool) -> Self { self.no_exponent_notation = flag; self } /// Set if a positive sign before the exponent is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_positive_exponent_sign(mut self, flag: bool) -> Self { self.no_positive_exponent_sign = flag; self } /// Set if a sign symbol before the exponent is required. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_sign(mut self, flag: bool) -> Self { self.required_exponent_sign = flag; self } /// Set if an exponent without fraction is not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_exponent_without_fraction(mut self, flag: bool) -> Self { self.no_exponent_without_fraction = flag; self } /// Set if special (non-finite) values are not allowed. + /// + /// Defaults to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_special(mut self, flag: bool) -> Self { self.no_special = flag; self } /// Set if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn case_sensitive_special(mut self, flag: bool) -> Self { self.case_sensitive_special = flag; self } /// Set if leading zeros before an integer are not allowed. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_integer_leading_zeros(mut self, flag: bool) -> Self { self.no_integer_leading_zeros = flag; self } /// Set if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_float_leading_zeros(mut self, flag: bool) -> Self { self.no_float_leading_zeros = flag; self } /// Set if exponent notation is required. + /// + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_notation(mut self, flag: bool) -> Self { self.required_exponent_notation = flag; self } /// Set if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn case_sensitive_exponent(mut self, flag: bool) -> Self { self.case_sensitive_exponent = flag; self } /// Set if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn case_sensitive_base_prefix(mut self, flag: bool) -> Self { self.case_sensitive_base_prefix = flag; self } /// Set if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn case_sensitive_base_suffix(mut self, flag: bool) -> Self { self.case_sensitive_base_suffix = flag; self @@ -787,9 +2022,27 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_internal_digit_separator(mut self, flag: bool) -> Self { self.integer_internal_digit_separator = flag; self @@ -799,9 +2052,26 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_internal_digit_separator(mut self, flag: bool) -> Self { self.fraction_internal_digit_separator = flag; self @@ -811,9 +2081,26 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_internal_digit_separator(mut self, flag: bool) -> Self { self.exponent_internal_digit_separator = flag; self @@ -823,9 +2110,16 @@ impl NumberFormatBuilder { /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Sets [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], and + /// [`exponent_internal_digit_separator`]. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn internal_digit_separator(mut self, flag: bool) -> Self { self = self.integer_internal_digit_separator(flag); self = self.fraction_internal_digit_separator(flag); @@ -836,9 +2130,27 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed before any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_leading_digit_separator(mut self, flag: bool) -> Self { self.integer_leading_digit_separator = flag; self @@ -847,9 +2159,26 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed before any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_leading_digit_separator(mut self, flag: bool) -> Self { self.fraction_leading_digit_separator = flag; self @@ -858,9 +2187,26 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed before any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_leading_digit_separator(mut self, flag: bool) -> Self { self.exponent_leading_digit_separator = flag; self @@ -869,9 +2215,17 @@ impl NumberFormatBuilder { /// Set all leading digit separator flags. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Sets + /// [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], and + /// [`exponent_leading_digit_separator`]. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn leading_digit_separator(mut self, flag: bool) -> Self { self = self.integer_leading_digit_separator(flag); self = self.fraction_leading_digit_separator(flag); @@ -882,9 +2236,27 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed after any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_trailing_digit_separator(mut self, flag: bool) -> Self { self.integer_trailing_digit_separator = flag; self @@ -893,9 +2265,25 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed after any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_trailing_digit_separator(mut self, flag: bool) -> Self { self.fraction_trailing_digit_separator = flag; self @@ -904,9 +2292,26 @@ impl NumberFormatBuilder { /// Set if a digit separator is allowed after any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_trailing_digit_separator(mut self, flag: bool) -> Self { self.exponent_trailing_digit_separator = flag; self @@ -915,9 +2320,17 @@ impl NumberFormatBuilder { /// Set all trailing digit separator flags. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Sets + /// [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], and + /// [`exponent_trailing_digit_separator`]. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn trailing_digit_separator(mut self, flag: bool) -> Self { self = self.integer_trailing_digit_separator(flag); self = self.fraction_trailing_digit_separator(flag); @@ -926,32 +2339,69 @@ impl NumberFormatBuilder { } /// Set if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_consecutive_digit_separator(mut self, flag: bool) -> Self { self.integer_consecutive_digit_separator = flag; self } /// Set if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_consecutive_digit_separator(mut self, flag: bool) -> Self { self.fraction_consecutive_digit_separator = flag; self } /// Set if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_consecutive_digit_separator(mut self, flag: bool) -> Self { self.exponent_consecutive_digit_separator = flag; self } /// Set all consecutive digit separator flags. + /// + /// Sets [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], and + /// [`exponent_consecutive_digit_separator`]. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn consecutive_digit_separator(mut self, flag: bool) -> Self { self = self.integer_consecutive_digit_separator(flag); self = self.fraction_consecutive_digit_separator(flag); @@ -960,16 +2410,35 @@ impl NumberFormatBuilder { } /// Set if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn special_digit_separator(mut self, flag: bool) -> Self { self.special_digit_separator = flag; self } - /// Set all digit separator flag masks. + /// Allow digit separators in all locations for all components. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the integer, fraction, and exponent components. Defaults + /// to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn digit_separator_flags(mut self, flag: bool) -> Self { self = self.integer_digit_separator_flags(flag); self = self.fraction_digit_separator_flags(flag); @@ -979,8 +2448,17 @@ impl NumberFormatBuilder { } /// Set all integer digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the integer component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_digit_separator_flags(mut self, flag: bool) -> Self { self = self.integer_internal_digit_separator(flag); self = self.integer_leading_digit_separator(flag); @@ -990,8 +2468,16 @@ impl NumberFormatBuilder { } /// Set all fraction digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the fraction component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_digit_separator_flags(mut self, flag: bool) -> Self { self = self.fraction_internal_digit_separator(flag); self = self.fraction_leading_digit_separator(flag); @@ -1001,8 +2487,16 @@ impl NumberFormatBuilder { } /// Set all exponent digit separator flag masks. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for the exponent component. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] #[cfg(feature = "format")] + #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_digit_separator_flags(mut self, flag: bool) -> Self { self = self.exponent_internal_digit_separator(flag); self = self.exponent_leading_digit_separator(flag); @@ -1015,12 +2509,16 @@ impl NumberFormatBuilder { /// Create 128-bit, packed number format struct from builder options. /// - /// NOTE: This function will never fail, due to issues with panicking - /// (and therefore unwrapping Errors/Options) in const fns. It is - /// therefore up to you to ensure the format is valid, called via the - /// `is_valid` function on `NumberFormat`. + ///
+ /// + /// This function will never fail. It is up to the caller to ensure the + /// format is valid using [`NumberFormat::is_valid`]. + /// + ///
+ /// + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid #[inline(always)] - pub const fn build(&self) -> u128 { + pub const fn build_unchecked(&self) -> u128 { let mut format: u128 = 0; add_flags!( format ; @@ -1069,6 +2567,44 @@ impl NumberFormatBuilder { format } + /// Build the packed number format, panicking if the builder is invalid. + /// + /// # Panics + /// + /// If the built format is not valid. + #[inline(always)] + pub const fn build_strict(&self) -> u128 { + use crate::format::format_error_impl; + + let packed = self.build_unchecked(); + match format_error_impl(packed) { + Error::Success => packed, + error => core::panic!("{}", error.description()), + } + } + + /// Create 128-bit, packed number format struct from builder options. + /// + ///
+ /// + /// This function will never fail. It is up to the caller to ensure the + /// format is valid using [`NumberFormat::is_valid`]. This function is + /// soft-deprecated and you should prefer [`build_unchecked`] and handle + /// if the result is invalid instead, or use [`build_strict`] to panic on + /// any errors. This exists when compatibility with older Rust + /// versions was required. + /// + ///
+ /// + /// [`build_unchecked`]: Self::build_unchecked + /// [`build_strict`]: Self::build_strict + /// [`NumberFormat::is_valid`]: crate::NumberFormat::is_valid + #[inline(always)] + #[deprecated = "Use `build_strict` or `build_unchecked` instead."] + pub const fn build(&self) -> u128 { + self.build_unchecked() + } + /// Re-create builder from format. #[inline(always)] pub const fn rebuild(format: u128) -> Self { diff --git a/lexical-util/src/format_flags.rs b/lexical-util/src/format_flags.rs index c55a36fc..9c6ba9c3 100644 --- a/lexical-util/src/format_flags.rs +++ b/lexical-util/src/format_flags.rs @@ -204,8 +204,7 @@ //! 44. `MongoDB` #![cfg_attr(rustfmt, rustfmt::skip)] - -use static_assertions::const_assert; +#![doc(hidden)] // ASSERTIONS // ---------- @@ -213,28 +212,28 @@ use static_assertions::const_assert; // Ensure all our bit flags are valid. macro_rules! check_subsequent_flags { ($x:ident, $y:ident) => { - const_assert!($x << 1 == $y); + const _: () = assert!($x << 1 == $y); }; } // Ensure all our bit masks don't overlap. macro_rules! check_subsequent_masks { ($x:ident, $y:ident) => { - const_assert!($x & $y == 0); + const _: () = assert!($x & $y == 0); }; } // Check all our masks are in the range `[0, 255]` after shifting. macro_rules! check_mask_shifts { ($mask:ident, $shift:ident) => { - const_assert!(0 < $mask >> $shift && 255 >= $mask >> $shift); + const _: () = assert!(0 < $mask >> $shift && 255 >= $mask >> $shift); }; } // Ensure all our bit masks don't overlap with existing flags. macro_rules! check_masks_and_flags { ($x:ident, $y:ident) => { - const_assert!($x & $y == 0); + const _: () = assert!($x & $y == 0); }; } @@ -338,7 +337,7 @@ pub const CASE_SENSITIVE_BASE_PREFIX: u128 = 1 << 16; pub const CASE_SENSITIVE_BASE_SUFFIX: u128 = 1 << 17; // Non-digit separator flags. -const_assert!(REQUIRED_INTEGER_DIGITS == 1); +const _: () = assert!(REQUIRED_INTEGER_DIGITS == 1); check_subsequent_flags!(REQUIRED_INTEGER_DIGITS, REQUIRED_FRACTION_DIGITS); check_subsequent_flags!(REQUIRED_FRACTION_DIGITS, REQUIRED_EXPONENT_DIGITS); check_subsequent_flags!(REQUIRED_EXPONENT_DIGITS, REQUIRED_MANTISSA_DIGITS); @@ -425,7 +424,7 @@ pub const CONSECUTIVE_DIGIT_SEPARATOR: u128 = pub const SPECIAL_DIGIT_SEPARATOR: u128 = 1 << 44; // Digit separator flags. -const_assert!(INTEGER_INTERNAL_DIGIT_SEPARATOR == 1 << 32); +const _: () = assert!(INTEGER_INTERNAL_DIGIT_SEPARATOR == 1 << 32); check_subsequent_flags!(INTEGER_INTERNAL_DIGIT_SEPARATOR, FRACTION_INTERNAL_DIGIT_SEPARATOR); check_subsequent_flags!(FRACTION_INTERNAL_DIGIT_SEPARATOR, EXPONENT_INTERNAL_DIGIT_SEPARATOR); check_subsequent_flags!(EXPONENT_INTERNAL_DIGIT_SEPARATOR, INTEGER_LEADING_DIGIT_SEPARATOR); @@ -466,10 +465,10 @@ pub const MANTISSA_RADIX_SHIFT: i32 = 104; /// Mask to extract the mantissa radix: the radix for the significant digits. pub const MANTISSA_RADIX: u128 = 0xFF << MANTISSA_RADIX_SHIFT; -/// Alias for `MANTISSA_RADIX_SHIFT`. +/// Alias for [`MANTISSA_RADIX_SHIFT`]. pub const RADIX_SHIFT: i32 = MANTISSA_RADIX_SHIFT; -/// Alias for `MANTISSA_RADIX`. +/// Alias for [`MANTISSA_RADIX`]. pub const RADIX: u128 = MANTISSA_RADIX; /// Shift to convert to and from an exponent base as a `u32`. @@ -484,6 +483,12 @@ pub const EXPONENT_RADIX_SHIFT: i32 = 120; /// Mask to extract the exponent radix: the radix for the exponent digits. pub const EXPONENT_RADIX: u128 = 0xFF << EXPONENT_RADIX_SHIFT; +/// Mask to extract the exponent radix: the radix for the exponent digits. +/// +/// This only extracts the radix bits, so negating it can be used +/// to see if any other custom settings were provided. +pub const RADIX_MASK: u128 = MANTISSA_RADIX | EXPONENT_RADIX; + // Masks do not overlap. check_subsequent_masks!(DIGIT_SEPARATOR, BASE_PREFIX); check_subsequent_masks!(BASE_PREFIX, BASE_SUFFIX); @@ -600,31 +605,37 @@ pub const EXPONENT_DIGIT_SEPARATOR_FLAG_MASK: u128 = // ---------- /// Extract the digit separator from the format packed struct. +#[doc(hidden)] #[inline(always)] pub const fn digit_separator(format: u128) -> u8 { ((format & DIGIT_SEPARATOR) >> DIGIT_SEPARATOR_SHIFT) as u8 } /// Extract the base prefix character from the format packed struct. +#[doc(hidden)] #[inline(always)] pub const fn base_prefix(format: u128) -> u8 { ((format & BASE_PREFIX) >> BASE_PREFIX_SHIFT) as u8 } /// Extract the base suffix character from the format packed struct. +#[doc(hidden)] #[inline(always)] pub const fn base_suffix(format: u128) -> u8 { ((format & BASE_SUFFIX) >> BASE_SUFFIX_SHIFT) as u8 } /// Extract the mantissa radix from the format packed struct. +#[doc(hidden)] #[inline(always)] pub const fn mantissa_radix(format: u128) -> u32 { ((format & MANTISSA_RADIX) >> MANTISSA_RADIX_SHIFT) as u32 } /// Extract the exponent base from the format packed struct. +/// /// If not provided, defaults to `mantissa_radix`. +#[doc(hidden)] #[inline(always)] pub const fn exponent_base(format: u128) -> u32 { let radix = ((format & EXPONENT_BASE) >> EXPONENT_BASE_SHIFT) as u32; @@ -636,7 +647,9 @@ pub const fn exponent_base(format: u128) -> u32 { } /// Extract the exponent radix from the format packed struct. +/// /// If not provided, defaults to `mantissa_radix`. +#[doc(hidden)] #[inline(always)] pub const fn exponent_radix(format: u128) -> u32 { let radix = ((format & EXPONENT_RADIX) >> EXPONENT_RADIX_SHIFT) as u32; @@ -648,6 +661,7 @@ pub const fn exponent_radix(format: u128) -> u32 { } /// Extract a generic radix from the format and bitflags. +#[doc(hidden)] #[inline(always)] pub const fn radix_from_flags(format: u128, mask: u128, shift: i32) -> u32 { let radix = ((format & mask) >> shift) as u32; @@ -672,6 +686,7 @@ pub const fn is_valid_exponent_flags(format: u128) -> bool { } /// Determine if an optional control character is valid. +#[doc(hidden)] #[inline(always)] const fn is_valid_optional_control_radix(radix: u32, value: u8) -> bool { // Validate the character isn't a digit or sign character, and is valid ASCII. @@ -684,6 +699,7 @@ const fn is_valid_optional_control_radix(radix: u32, value: u8) -> bool { } /// Determine if an optional control character is valid. +#[doc(hidden)] #[inline(always)] const fn is_valid_optional_control(format: u128, value: u8) -> bool { // Need to get the larger of the two radix values, since these @@ -700,12 +716,14 @@ const fn is_valid_optional_control(format: u128, value: u8) -> bool { } /// Determine if an control character is valid. +#[doc(hidden)] #[inline(always)] const fn is_valid_control(format: u128, value: u8) -> bool { value != 0 && is_valid_optional_control(format, value) } /// Determine if the digit separator is valid. +/// /// Digit separators must not be valid digits or sign characters. #[inline(always)] pub const fn is_valid_digit_separator(format: u128) -> bool { @@ -721,7 +739,7 @@ pub const fn is_valid_digit_separator(format: u128) -> bool { #[inline(always)] pub const fn is_valid_base_prefix(format: u128) -> bool { let value = base_prefix(format); - if cfg!(feature = "format") { + if cfg!(all(feature = "format", feature = "power-of-two")) { is_valid_optional_control(format, value) } else { value == 0 @@ -732,7 +750,7 @@ pub const fn is_valid_base_prefix(format: u128) -> bool { #[inline(always)] pub const fn is_valid_base_suffix(format: u128) -> bool { let value = base_suffix(format); - if cfg!(feature = "format") { + if cfg!(all(feature = "format", feature = "power-of-two")) { is_valid_optional_control(format, value) } else { value == 0 @@ -765,6 +783,7 @@ pub const fn is_valid_punctuation(format: u128) -> bool { } /// Determine if all of the "punctuation" characters for the options API are valid. +#[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else)] // reason="all are different logic conditions" #[allow(clippy::needless_bool)] // reason="not needless depending on the format condition" @@ -795,6 +814,7 @@ pub const fn is_valid_options_punctuation(format: u128, exponent: u8, decimal_po } /// Determine if the radix is valid. +#[inline(always)] pub const fn is_valid_radix(radix: u32) -> bool { if cfg!(feature = "radix") { radix >= 2 && radix <= 36 diff --git a/lexical-util/src/iterator.rs b/lexical-util/src/iterator.rs index 6a628811..6077de98 100644 --- a/lexical-util/src/iterator.rs +++ b/lexical-util/src/iterator.rs @@ -5,6 +5,7 @@ //! including containers or iterators of any kind. #![cfg(feature = "parse")] +#![cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] use core::mem; @@ -35,7 +36,6 @@ pub use crate::skip::{AsBytes, Bytes}; /// [`step_by_unchecked`]: `Iter::step_by_unchecked` /// [`step_unchecked`]: `Iter::step_unchecked` /// [`peek_many_unchecked`]: `Iter::peek_many_unchecked` -#[cfg(feature = "parse")] pub unsafe trait Iter<'a> { /// Determine if the buffer is contiguous in memory. const IS_CONTIGUOUS: bool; diff --git a/lexical-util/src/lib.rs b/lexical-util/src/lib.rs index 22e2cec5..d73198ae 100644 --- a/lexical-util/src/lib.rs +++ b/lexical-util/src/lib.rs @@ -7,97 +7,69 @@ //! //! # Features //! -//! * `std` - Use the standard library. -//! * `power-of-two` - Add support for parsing power-of-two integer strings. -//! * `radix` - Add support for strings of any radix. -//! * `write-integers` - Add support for writing integers. -//! * `write-floats` - Add support for writing floats. -//! * `parse-integers` - Add support for parsing integers. -//! * `parse-floats` - Add support for parsing floats. +//! * `power-of-two` - Add support for parsing and writing power-of-two integer +//! strings. +//! * `radix` - Add support for parsing and writing strings of any radix. +//! * `format` - Add support for custom number formats. +//! * `write-integers` - Add support for writing integers (used for +//! [`lexical-write-integer`]). +//! * `write-floats` - Add support for writing floats (used for +//! [`lexical-write-float`]). +//! * `parse-integers` - Add support for parsing integers (used for +//! [`lexical-parse-integer`]). +//! * `parse-floats` - Add support for parsing floats (used for +//! [`lexical-write-float`]). //! * `compact` - Reduce code size at the cost of performance. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. //! -//! # Note +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format //! -//! None of this is considered a public API: any of the implementation -//! details may change release-to-release without major or minor version -//! changes. Use internal implementation details at your own risk. +//! # Public API //! -//! lexical-util mainly exists as an implementation detail for -//! lexical-core, although its API is stable. If you would like to use -//! a high-level API that writes to and parses from `String` and `&str`, -//! respectively, please look at [lexical](https://crates.io/crates/lexical) -//! instead. If you would like an API that supports multiple numeric -//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) -//! instead. +//! [`lexical-util`] mainly exists as an implementation detail for +//! the other lexical crates, although its API is mostly stable. If you would +//! like to use a high-level API that writes to and parses from [`String`] and +//! [`str`], respectively, please look at [`lexical`] instead. If you would like +//! an API that supports multiple numeric conversions without a dependency on +//! [`alloc`], please look at [`lexical-core`] instead. //! -//! # Version Support -//! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. -//! -//! # Safety Guarantees -//! -//! The only major sources of unsafe code are wrapped in the `iterator.rs`, -//! `skip.rs`, and `noskip.rs`. These are fully encapsulated into standalone -//! traits to clearly define safety invariants and localize any unsafety to -//! 1 or 2 lines of code. -//! -//! The core, unsafe trait is `DigitsIter` and `Iter`, both which expect -//! to be backed by a contiguous block of memory (a slice) but may skip -//! bytes internally. To guarantee safety, for non-skip iterators you -//! must implement [DigitsIter::is_consumed][is_consumed] correctly. +//!
//! -//! This must correctly determine if there are any elements left in the -//! iterator. If the buffer is contiguous, this can just be `index == -//! self.len()`, but for a non-contiguous iterator it must skip any digits to -//! advance to the element next to be returned or the iterator itself will be -//! unsafe. **ALL** other safety invariants depend on this being implemented -//! correctly. +//! Any undocumented, implementation details may change release-to-release +//! without major or minor version changes. Use internal implementation details +//! at your own risk. Any changes other than to [`NumberFormatBuilder`], +//! [`NumberFormat`], [`mod@format`], and [`mod@options`] will not be considered +//! a breaking change. //! -//! To see if the cursor is at the end of the buffer, use -//! [is_buffer_empty][is_buffer_empty]. +//!
//! -//! Any iterators must be peekable: you must be able to read and return the next -//! value without advancing the iterator past that point. For iterators that -//! skip bytes, this means advancing to the next element to be returned and -//! returning that value. -//! -//! For examples of how to safely implement skip iterators, you can do something -//! like: -//! -//! ```rust,ignore -//! impl<_> DigitsIter<_> for MyIter { -//! fn peek(&mut self) -> Option { -//! loop { -//! let value = self.bytes.get(self.index)?; -//! if value != &b'.' { -//! return value; -//! } -//! self.index += 1; -//! } -//! } -//! } -//! ``` -//! -//! Then, [next](core::iter::Iterator::next) will be implemented in terms -//! of [peek], incrementing the position in the cursor just after the value. -//! The next iteration of peek will step to the correct byte to return. +//! # Version Support //! -//! ```rust,ignore -//! impl<_> Iterator for MyIter { -//! type Item = &'a u8; +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. //! -//! fn next(&mut self) -> Option { -//! let value = self.peek()?; -//! self.index += 1; -//! Some(value) -//! } -//! } -//! ``` +//! # Safety Guarantees //! -//! [is_buffer_empty]: -//! [is_consumed]: -//! [peek]: +//! For a detailed breakdown on the use of [`unsafe`], how and why our traits +//! are implemented safely, and how to verify this, see [`Safety`]. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-parse-float`]: https://crates.io/crates/lexical-parse-float +//! [`lexical-parse-integer`]: https://crates.io/crates/lexical-parse-integer +//! [`lexical-write-float`]: https://crates.io/crates/lexical-write-float +//! [`lexical-write-integer`]: https://crates.io/crates/lexical-write-integer +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! [`lexical-util`]: https://crates.io/crates/lexical-util +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html +//! [`alloc`]: https://doc.rust-lang.org/alloc/ +//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html +//! [`Safety`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/docs/Safety.md +//! [`unsafe`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html // FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below // Clippy reasons were stabilized in 1.81.0. @@ -107,6 +79,8 @@ #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -177,3 +151,13 @@ mod format_flags; mod noskip; mod not_feature_format; mod skip; + +#[cfg(feature = "write")] +pub use constants::{FormattedSize, BUFFER_SIZE}; +pub use error::Error; +pub use format::{NumberFormat, NumberFormatBuilder}; +#[cfg(feature = "parse")] +pub use options::ParseOptions; +#[cfg(feature = "write")] +pub use options::WriteOptions; +pub use result::Result; diff --git a/lexical-util/src/mul.rs b/lexical-util/src/mul.rs index ce1afcb7..1ea9e923 100644 --- a/lexical-util/src/mul.rs +++ b/lexical-util/src/mul.rs @@ -2,10 +2,11 @@ use crate::num::{as_cast, UnsignedInteger}; -/// Multiply two unsigned, integral values, and return the hi and lo product. +/// Multiply two unsigned, integral values, and return the high and low product. /// -/// The `full` type is the full type size, while the `half` type is the type -/// with exactly half the bits. +/// The high product is the upper half of the product and the low product is the +/// lower half. The `full` type is the full type size, while the `half` type is +/// the type with exactly half the bits. #[inline(always)] pub fn mul(x: Full, y: Full) -> (Full, Full) where @@ -29,10 +30,11 @@ where (hi, lo) } -/// Multiply two unsigned, integral values, and return the hi product. +/// Multiply two unsigned, integral values, and return the high product. /// -/// The `full` type is the full type size, while the `half` type is the type -/// with exactly half the bits. +/// The high product is the upper half of the product. The `full` type is the +/// full type size, while the `half` type is the type with exactly half the +/// bits. #[inline(always)] pub fn mulhi(x: Full, y: Full) -> Full where diff --git a/lexical-util/src/not_feature_format.rs b/lexical-util/src/not_feature_format.rs index 23f9a2f6..c3949e85 100644 --- a/lexical-util/src/not_feature_format.rs +++ b/lexical-util/src/not_feature_format.rs @@ -1,5 +1,5 @@ //! Bare bones implementation of the format packed struct without feature -//! `format`. +//! [`format`][crate#features]. //! //! See `feature_format` for detailed documentation. @@ -9,60 +9,71 @@ use crate::error::Error; use crate::format_builder::NumberFormatBuilder; use crate::format_flags as flags; -/// Wrapper for the 128-bit packed struct. +/// Helper to access features from the packed format struct. +/// +/// Some of the core functionality includes support for: +/// - Digit separators: ignored characters used to make numbers more readable, +/// such as `100,000`. +/// - Non-decimal radixes: writing or parsing numbers written in binary, +/// hexadecimal, or other bases. +/// - Special numbers: disabling support for special floating-point, such as +/// [`NaN`][f64::NAN]. +/// - Number components: require signs, significant digits, and more. /// /// The following values are explicitly set, and therefore not configurable: -/// 1. required_integer_digits -/// 2. required_fraction_digits -/// 3. required_exponent_digits -/// 4. required_mantissa_digits -/// 5. required_digits -/// 6. no_positive_mantissa_sign -/// 7. required_mantissa_sign -/// 8. no_exponent_notation -/// 9. no_positive_exponent_sign -/// 10. required_exponent_sign -/// 11. no_exponent_without_fraction -/// 12. no_special -/// 13. case_sensitive_special -/// 14. no_integer_leading_zeros -/// 15. no_float_leading_zeros -/// 16. required_exponent_notation -/// 17. case_sensitive_exponent -/// 18. case_sensitive_base_prefix -/// 19. case_sensitive_base_suffix -/// 20. integer_internal_digit_separator -/// 21. fraction_internal_digit_separator -/// 22. exponent_internal_digit_separator -/// 23. internal_digit_separator -/// 24. integer_leading_digit_separator -/// 25. fraction_leading_digit_separator -/// 26. exponent_leading_digit_separator -/// 27. leading_digit_separator -/// 28. integer_trailing_digit_separator -/// 29. fraction_trailing_digit_separator -/// 30. exponent_trailing_digit_separator -/// 31. trailing_digit_separator -/// 32. integer_consecutive_digit_separator -/// 33. fraction_consecutive_digit_separator -/// 34. exponent_consecutive_digit_separator -/// 35. consecutive_digit_separator -/// 36. special_digit_separator -/// 37. digit_separator -/// 38. base_prefix -/// 39. base_suffix -/// 40. exponent_base -/// 41. exponent_radix /// -/// See `NumberFormatBuilder` for the `FORMAT` fields -/// for the packed struct. -#[doc(hidden)] +/// 1. [`required_integer_digits`][NumberFormat::required_integer_digits] +/// 2. [`required_fraction_digits`][NumberFormat::required_fraction_digits] +/// 3. [`required_exponent_digits`][NumberFormat::required_exponent_digits] +/// 4. [`required_mantissa_digits`][NumberFormat::required_mantissa_digits] +/// 5. [`required_digits`][NumberFormat::required_digits] +/// 6. [`no_positive_mantissa_sign`][NumberFormat::no_positive_mantissa_sign] +/// 7. [`required_mantissa_sign`][NumberFormat::required_mantissa_sign] +/// 8. [`no_exponent_notation`][NumberFormat::no_exponent_notation] +/// 9. [`no_positive_exponent_sign`][NumberFormat::no_positive_exponent_sign] +/// 10. [`required_exponent_sign`][NumberFormat::required_exponent_sign] +/// 11. [`no_exponent_without_fraction`][NumberFormat::no_exponent_without_fraction] +/// 12. [`no_special`][NumberFormat::no_special] +/// 13. [`case_sensitive_special`][NumberFormat::case_sensitive_special] +/// 14. [`no_integer_leading_zeros`][NumberFormat::no_integer_leading_zeros] +/// 15. [`no_float_leading_zeros`][NumberFormat::no_float_leading_zeros] +/// 16. [`required_exponent_notation`][NumberFormat::required_exponent_notation] +/// 17. [`case_sensitive_exponent`][NumberFormat::case_sensitive_exponent] +/// 18. [`case_sensitive_base_prefix`][NumberFormat::case_sensitive_base_prefix] +/// 19. [`case_sensitive_base_suffix`][NumberFormat::case_sensitive_base_suffix] +/// 20. [`integer_internal_digit_separator`][NumberFormat::integer_internal_digit_separator] +/// 21. [`fraction_internal_digit_separator`][NumberFormat::fraction_internal_digit_separator] +/// 22. [`exponent_internal_digit_separator`][NumberFormat::exponent_internal_digit_separator] +/// 23. [`internal_digit_separator`][NumberFormat::internal_digit_separator] +/// 24. [`integer_leading_digit_separator`][NumberFormat::integer_leading_digit_separator] +/// 25. [`fraction_leading_digit_separator`][NumberFormat::fraction_leading_digit_separator] +/// 26. [`exponent_leading_digit_separator`][NumberFormat::exponent_leading_digit_separator] +/// 27. [`leading_digit_separator`][NumberFormat::leading_digit_separator] +/// 28. [`integer_trailing_digit_separator`][NumberFormat::integer_trailing_digit_separator] +/// 29. [`fraction_trailing_digit_separator`][NumberFormat::fraction_trailing_digit_separator] +/// 30. [`exponent_trailing_digit_separator`][NumberFormat::exponent_trailing_digit_separator] +/// 31. [`trailing_digit_separator`][NumberFormat::trailing_digit_separator] +/// 32. [`integer_consecutive_digit_separator`][NumberFormat::integer_consecutive_digit_separator] +/// 33. [`fraction_consecutive_digit_separator`][NumberFormat::fraction_consecutive_digit_separator] +/// 34. [`exponent_consecutive_digit_separator`][NumberFormat::exponent_consecutive_digit_separator] +/// 35. [`consecutive_digit_separator`][NumberFormat::consecutive_digit_separator] +/// 36. [`special_digit_separator`][NumberFormat::special_digit_separator] +/// 37. [`digit_separator`][NumberFormat::digit_separator] +/// 38. [`base_prefix`][NumberFormat::base_prefix] +/// 39. [`base_suffix`][NumberFormat::base_suffix] +/// 40. [`exponent_base`][NumberFormat::exponent_base] +/// 41. [`exponent_radix`][NumberFormat::exponent_radix] +/// +/// This should always be constructed via [`NumberFormatBuilder`]. +/// See [`NumberFormatBuilder`] for the fields for the packed struct. pub struct NumberFormat; impl NumberFormat { // CONSTRUCTORS /// Create new instance (for methods and validation). + /// + /// This uses the same settings as in the `FORMAT` packed struct. pub const fn new() -> Self { Self {} } @@ -76,196 +87,487 @@ impl NumberFormat { /// Get the error type from the format. pub const fn error(&self) -> Error { - let valid_flags = flags::REQUIRED_EXPONENT_DIGITS | flags::REQUIRED_MANTISSA_DIGITS; - if !flags::is_valid_radix(self.mantissa_radix()) { - Error::InvalidMantissaRadix - } else if !flags::is_valid_radix(self.exponent_base()) { - Error::InvalidExponentBase - } else if !flags::is_valid_radix(self.exponent_radix()) { - Error::InvalidExponentRadix - } else if !flags::is_valid_digit_separator(FORMAT) { - Error::InvalidDigitSeparator - } else if !flags::is_valid_base_prefix(FORMAT) { - Error::InvalidBasePrefix - } else if !flags::is_valid_base_suffix(FORMAT) { - Error::InvalidBaseSuffix - } else if !flags::is_valid_punctuation(FORMAT) { - Error::InvalidPunctuation - } else if self.flags() != valid_flags { - Error::InvalidFlags - } else { - Error::Success - } + format_error_impl(FORMAT) } // NON-DIGIT SEPARATOR FLAGS & MASKS /// If digits are required before the decimal point. + /// + /// See [`required_integer_digits`][Self::required_integer_digits]. pub const REQUIRED_INTEGER_DIGITS: bool = false; /// Get if digits are required before the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `0.1` | ✔️ | + /// | `1` | ✔️ | + /// | `.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_integer_digits(&self) -> bool { Self::REQUIRED_INTEGER_DIGITS } /// If digits are required after the decimal point. + /// + /// See [`required_fraction_digits`][Self::required_fraction_digits]. pub const REQUIRED_FRACTION_DIGITS: bool = false; /// Get if digits are required after the decimal point. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1` | ✔️ | + /// | `1.` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_fraction_digits(&self) -> bool { Self::REQUIRED_FRACTION_DIGITS } /// If digits are required after the exponent character. + /// + /// See [`required_exponent_digits`][Self::required_exponent_digits]. pub const REQUIRED_EXPONENT_DIGITS: bool = true; /// Get if digits are required after the exponent character. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e+3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// | `1.1e+` | ❌ | + /// | `1.1e` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_exponent_digits(&self) -> bool { Self::REQUIRED_EXPONENT_DIGITS } /// If significant digits are required. + /// + /// See [`required_mantissa_digits`][Self::required_mantissa_digits]. pub const REQUIRED_MANTISSA_DIGITS: bool = true; - /// Get if significant digits are required. + /// Get if at least 1 significant digit is required. + /// + /// If not required, then values like `.` (`0`) are valid, but empty strings + /// are still invalid. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`true`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `.` | ✔️ | + /// | `e10` | ✔️ | + /// | `.e10` | ✔️ | + /// | `` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn required_mantissa_digits(&self) -> bool { Self::REQUIRED_MANTISSA_DIGITS } /// If at least 1 digit in the number is required. + /// + /// See [`required_digits`][Self::required_digits]. pub const REQUIRED_DIGITS: bool = true; /// Get if at least 1 digit in the number is required. + /// + /// This requires either [`mantissa`] or [`exponent`] digits. + /// + /// [`mantissa`]: Self::required_mantissa_digits + /// [`exponent`]: Self::required_exponent_digits #[inline(always)] pub const fn required_digits(&self) -> bool { Self::REQUIRED_DIGITS } /// If a positive sign before the mantissa is not allowed. + /// + /// See [`no_positive_mantissa_sign`][Self::no_positive_mantissa_sign]. pub const NO_POSITIVE_MANTISSA_SIGN: bool = false; /// Get if a positive sign before the mantissa is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn no_positive_mantissa_sign(&self) -> bool { Self::NO_POSITIVE_MANTISSA_SIGN } /// If a sign symbol before the mantissa is required. + /// + /// See [`required_mantissa_sign`][Self::required_mantissa_sign]. pub const REQUIRED_MANTISSA_SIGN: bool = false; /// Get if a sign symbol before the mantissa is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ❌ | + /// | `-1.1` | ✔️ | + /// | `+1.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float #[inline(always)] pub const fn required_mantissa_sign(&self) -> bool { Self::REQUIRED_MANTISSA_SIGN } /// If exponent notation is not allowed. + /// + /// See [`no_exponent_notation`][Self::no_exponent_notation]. pub const NO_EXPONENT_NOTATION: bool = false; /// Get if exponent notation is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1.1` | ✔️ | + /// | `1.1e` | ❌ | + /// | `1.1e5` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn no_exponent_notation(&self) -> bool { Self::NO_EXPONENT_NOTATION } /// If a positive sign before the exponent is not allowed. + /// + /// See [`no_positive_exponent_sign`][Self::no_positive_exponent_sign]. pub const NO_POSITIVE_EXPONENT_SIGN: bool = false; /// Get if a positive sign before the exponent is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ✔️ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn no_positive_exponent_sign(&self) -> bool { Self::NO_POSITIVE_EXPONENT_SIGN } /// If a sign symbol before the exponent is required. + /// + /// See [`required_exponent_sign`][Self::required_exponent_sign]. pub const REQUIRED_EXPONENT_SIGN: bool = false; /// Get if a sign symbol before the exponent is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e3` | ❌ | + /// | `1.1e-3` | ✔️ | + /// | `1.1e+3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn required_exponent_sign(&self) -> bool { Self::REQUIRED_EXPONENT_SIGN } /// If an exponent without fraction is not allowed. + /// + /// See [`no_exponent_without_fraction`][Self::no_exponent_without_fraction]. pub const NO_EXPONENT_WITHOUT_FRACTION: bool = false; /// Get if an exponent without fraction is not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1e3` | ❌ | + /// | `1.e3` | ❌ | + /// | `1.1e` | ✔️ | + /// | `.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_exponent_without_fraction(&self) -> bool { Self::NO_EXPONENT_WITHOUT_FRACTION } /// If special (non-finite) values are not allowed. + /// + /// See [`no_special`][Self::no_special]. pub const NO_SPECIAL: bool = false; /// Get if special (non-finite) values are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `false`. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `NaN` | ❌ | + /// | `inf` | ❌ | + /// | `-Infinity` | ❌ | + /// | `1.1e` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_special(&self) -> bool { Self::NO_SPECIAL } /// If special (non-finite) values are case-sensitive. + /// + /// See [`case_sensitive_special`][Self::case_sensitive_special]. pub const CASE_SENSITIVE_SPECIAL: bool = false; /// Get if special (non-finite) values are case-sensitive. + /// + /// If set to [`true`], then `NaN` and `nan` are treated as the same value + /// ([Not a Number][f64::NAN]). Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn case_sensitive_special(&self) -> bool { Self::CASE_SENSITIVE_SPECIAL } /// If leading zeros before an integer are not allowed. + /// + /// See [`no_integer_leading_zeros`][Self::no_integer_leading_zeros]. pub const NO_INTEGER_LEADING_ZEROS: bool = false; /// Get if leading zeros before an integer are not allowed. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// + /// # Used For + /// + /// - Parse Integer #[inline(always)] pub const fn no_integer_leading_zeros(&self) -> bool { Self::NO_INTEGER_LEADING_ZEROS } /// If leading zeros before a float are not allowed. + /// + /// See [`no_float_leading_zeros`][Self::no_float_leading_zeros]. pub const NO_FLOAT_LEADING_ZEROS: bool = false; /// Get if leading zeros before a float are not allowed. + /// + /// This is before the significant digits of the float, that is, if there is + /// 1 or more digits in the integral component and the leading digit is 0, + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `01` | ❌ | + /// | `01.0` | ❌ | + /// | `0` | ✔️ | + /// | `10` | ✔️ | + /// | `0.1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn no_float_leading_zeros(&self) -> bool { Self::NO_FLOAT_LEADING_ZEROS } /// If exponent notation is required. + /// + /// See [`required_exponent_notation`][Self::required_exponent_notation]. pub const REQUIRED_EXPONENT_NOTATION: bool = false; /// Get if exponent notation is required. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to [`false`]. + /// + /// # Examples + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ❌ | + /// | `1.0` | ❌ | + /// | `1e3` | ✔️ | + /// | `1.1e3` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Write Float #[inline(always)] pub const fn required_exponent_notation(&self) -> bool { Self::REQUIRED_EXPONENT_NOTATION } /// If exponent characters are case-sensitive. + /// + /// See [`case_sensitive_exponent`][Self::case_sensitive_exponent]. pub const CASE_SENSITIVE_EXPONENT: bool = false; /// Get if exponent characters are case-sensitive. + /// + /// If set to [`true`], then the exponent character `e` would be considered + /// the different from `E`. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn case_sensitive_exponent(&self) -> bool { Self::CASE_SENSITIVE_EXPONENT } /// If base prefixes are case-sensitive. + /// + /// See [`case_sensitive_base_prefix`][Self::case_sensitive_base_prefix]. pub const CASE_SENSITIVE_BASE_PREFIX: bool = false; /// Get if base prefixes are case-sensitive. + /// + /// If set to [`true`], then the base prefix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn case_sensitive_base_prefix(&self) -> bool { Self::CASE_SENSITIVE_BASE_PREFIX } /// If base suffixes are case-sensitive. + /// + /// See [`case_sensitive_base_suffix`][Self::case_sensitive_base_suffix]. pub const CASE_SENSITIVE_BASE_SUFFIX: bool = false; /// Get if base suffixes are case-sensitive. + /// + /// If set to [`true`], then the base suffix `x` would be considered the + /// different from `X`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn case_sensitive_base_suffix(&self) -> bool { Self::CASE_SENSITIVE_BASE_SUFFIX @@ -278,13 +580,33 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`integer_internal_digit_separator`][Self::integer_internal_digit_separator]. pub const INTEGER_INTERNAL_DIGIT_SEPARATOR: bool = false; /// Get if digit separators are allowed between integer digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ✔️ | + /// | `1_` | ❌ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_internal_digit_separator(&self) -> bool { Self::INTEGER_INTERNAL_DIGIT_SEPARATOR @@ -295,13 +617,32 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`fraction_internal_digit_separator`][Self::fraction_internal_digit_separator]. pub const FRACTION_INTERNAL_DIGIT_SEPARATOR: bool = false; /// Get if digit separators are allowed between fraction digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ✔️ | + /// | `1.1_` | ❌ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_internal_digit_separator(&self) -> bool { Self::FRACTION_INTERNAL_DIGIT_SEPARATOR @@ -312,13 +653,32 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`exponent_internal_digit_separator`][Self::exponent_internal_digit_separator]. pub const EXPONENT_INTERNAL_DIGIT_SEPARATOR: bool = false; /// Get if digit separators are allowed between exponent digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ✔️ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_internal_digit_separator(&self) -> bool { Self::EXPONENT_INTERNAL_DIGIT_SEPARATOR @@ -329,13 +689,22 @@ impl NumberFormat { /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by /// digits. + /// + /// See [`internal_digit_separator`][Self::internal_digit_separator]. pub const INTERNAL_DIGIT_SEPARATOR: bool = false; /// Get if digit separators are allowed between digits. /// /// This will not consider an input of only the digit separator /// to be a valid separator: the digit separator must be surrounded by - /// digits. + /// digits. This is equivalent to any of + /// [`integer_internal_digit_separator`], + /// [`fraction_internal_digit_separator`], or + /// [`exponent_internal_digit_separator`] being set. + /// + /// [`integer_internal_digit_separator`]: Self::integer_internal_digit_separator + /// [`fraction_internal_digit_separator`]: Self::fraction_internal_digit_separator + /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator #[inline(always)] pub const fn internal_digit_separator(&self) -> bool { Self::INTERNAL_DIGIT_SEPARATOR @@ -345,12 +714,32 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`integer_leading_digit_separator`][Self::integer_leading_digit_separator]. pub const INTEGER_LEADING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed before any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ❌ | + /// | `_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_leading_digit_separator(&self) -> bool { Self::INTEGER_LEADING_DIGIT_SEPARATOR @@ -360,12 +749,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`fraction_leading_digit_separator`][Self::fraction_leading_digit_separator]. pub const FRACTION_LEADING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed before any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ❌ | + /// | `1._1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_leading_digit_separator(&self) -> bool { Self::FRACTION_LEADING_DIGIT_SEPARATOR @@ -375,12 +783,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`exponent_leading_digit_separator`][Self::exponent_leading_digit_separator]. pub const EXPONENT_LEADING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed before any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ❌ | + /// | `1.1e_1` | ✔️ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_leading_digit_separator(&self) -> bool { Self::EXPONENT_LEADING_DIGIT_SEPARATOR @@ -390,12 +817,21 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`leading_digit_separator`][Self::leading_digit_separator]. pub const LEADING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed before any digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_leading_digit_separator`], + /// [`fraction_leading_digit_separator`], or + /// [`exponent_leading_digit_separator`] being set. + /// + /// [`integer_leading_digit_separator`]: Self::integer_leading_digit_separator + /// [`fraction_leading_digit_separator`]: Self::fraction_leading_digit_separator + /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator #[inline(always)] pub const fn leading_digit_separator(&self) -> bool { Self::LEADING_DIGIT_SEPARATOR @@ -405,12 +841,32 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`integer_trailing_digit_separator`][Self::integer_trailing_digit_separator]. pub const INTEGER_TRAILING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed after any integer digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `_` | ❌ | + /// | `1_1` | ❌ | + /// | `1_` | ✔️ | + /// | `_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_trailing_digit_separator(&self) -> bool { Self::INTEGER_TRAILING_DIGIT_SEPARATOR @@ -420,12 +876,29 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`fraction_trailing_digit_separator`][Self::fraction_trailing_digit_separator]. pub const FRACTION_TRAILING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed after any fraction digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1` | ✔️ | + /// | `1._` | ❌ | + /// | `1.1_1` | ❌ | + /// | `1.1_` | ✔️ | + /// | `1._1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_trailing_digit_separator(&self) -> bool { Self::FRACTION_TRAILING_DIGIT_SEPARATOR @@ -435,12 +908,31 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`exponent_trailing_digit_separator`][Self::exponent_trailing_digit_separator]. pub const EXPONENT_TRAILING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed after any exponent digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. Can only be modified with + /// [`feature`][crate#features] `format`. Defaults to [`false`]. + /// + /// # Examples + /// + /// Using a digit separator of `_`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1.1e1` | ✔️ | + /// | `1.1e_` | ❌ | + /// | `1.1e1_1` | ❌ | + /// | `1.1e1_` | ✔️ | + /// | `1.1e_1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_trailing_digit_separator(&self) -> bool { Self::EXPONENT_TRAILING_DIGIT_SEPARATOR @@ -450,57 +942,121 @@ impl NumberFormat { /// /// This will consider an input of only the digit separator /// to be a identical to empty input. + /// + /// See [`trailing_digit_separator`][Self::trailing_digit_separator]. pub const TRAILING_DIGIT_SEPARATOR: bool = false; /// Get if a digit separator is allowed after any digits. /// /// This will consider an input of only the digit separator - /// to be a identical to empty input. + /// to be a identical to empty input. This is equivalent to + /// any of [`integer_trailing_digit_separator`], + /// [`fraction_trailing_digit_separator`], or + /// [`exponent_trailing_digit_separator`] being set. + /// + /// [`integer_trailing_digit_separator`]: Self::integer_trailing_digit_separator + /// [`fraction_trailing_digit_separator`]: Self::fraction_trailing_digit_separator + /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator #[inline(always)] pub const fn trailing_digit_separator(&self) -> bool { Self::TRAILING_DIGIT_SEPARATOR } /// If multiple consecutive integer digit separators are allowed. + /// + /// See [`integer_consecutive_digit_separator`][Self::integer_consecutive_digit_separator]. pub const INTEGER_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; /// Get if multiple consecutive integer digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// integer. Can only be modified with [`feature`][crate#features] `format`. + /// Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn integer_consecutive_digit_separator(&self) -> bool { Self::INTEGER_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive fraction digit separators are allowed. + /// + /// See [`fraction_consecutive_digit_separator`][Self::fraction_consecutive_digit_separator]. pub const FRACTION_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; /// Get if multiple consecutive fraction digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// fraction. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn fraction_consecutive_digit_separator(&self) -> bool { Self::FRACTION_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive exponent digit separators are allowed. + /// + /// See [`exponent_consecutive_digit_separator`][Self::exponent_consecutive_digit_separator]. pub const EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR: bool = false; /// Get if multiple consecutive exponent digit separators are allowed. + /// + /// That is, using `_` as a digit separator `__` would be allowed where any + /// digit separators (leading, trailing, internal) are allowed in the + /// exponent. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn exponent_consecutive_digit_separator(&self) -> bool { Self::EXPONENT_CONSECUTIVE_DIGIT_SEPARATOR } /// If multiple consecutive digit separators are allowed. + /// + /// See [`consecutive_digit_separator`][Self::consecutive_digit_separator]. pub const CONSECUTIVE_DIGIT_SEPARATOR: bool = false; /// Get if multiple consecutive digit separators are allowed. + /// + /// This is equivalent to any of [`integer_consecutive_digit_separator`], + /// [`fraction_consecutive_digit_separator`], or + /// [`exponent_consecutive_digit_separator`] being set. + /// + /// [`integer_consecutive_digit_separator`]: Self::integer_consecutive_digit_separator + /// [`fraction_consecutive_digit_separator`]: Self::fraction_consecutive_digit_separator + /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator #[inline(always)] pub const fn consecutive_digit_separator(&self) -> bool { Self::CONSECUTIVE_DIGIT_SEPARATOR } /// If any digit separators are allowed in special (non-finite) values. + /// + /// See [`special_digit_separator`][Self::special_digit_separator]. pub const SPECIAL_DIGIT_SEPARATOR: bool = false; /// Get if any digit separators are allowed in special (non-finite) values. + /// + /// This enables leading, trailing, internal, and consecutive digit + /// separators for any special floats: for example, `N__a_N_` is considered + /// the same as `NaN`. Can only be modified with [`feature`][crate#features] + /// `format`. Defaults to [`false`]. + /// + /// # Used For + /// + /// - Parse Float #[inline(always)] pub const fn special_digit_separator(&self) -> bool { Self::SPECIAL_DIGIT_SEPARATOR @@ -509,25 +1065,77 @@ impl NumberFormat { // CHARACTERS /// The digit separator character in the packed struct. + /// + /// See [`digit_separator`][Self::digit_separator]. pub const DIGIT_SEPARATOR: u8 = 0; - /// Get the digit separator character. + /// Get the digit separator for the number format. + /// + /// Digit separators are frequently used in number literals to group + /// digits: `1,000,000` is a lot more readable than `1000000`, but + /// the `,` characters should be ignored in the parsing of the number. + /// + /// Can only be modified with [`feature`][crate#features] `format`. Defaults + /// to `0`, or no digit separators allowed. + /// + /// # Examples + /// + /// Using a digit separator of `_` (note that the validity + /// oh where a digit separator can appear depends on the other digit + /// separator flags). + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1_4` | ✔️ | + /// | `+_14` | ✔️ | + /// | `+14e3_5` | ✔️ | + /// | `1_d` | ❌ | + /// + /// # Used For /// - /// If the digit separator is 0, digit separators are not allowed. + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn digit_separator(&self) -> u8 { Self::DIGIT_SEPARATOR } + /// Get if the format has a digit separator. + #[inline(always)] + pub const fn has_digit_separator(&self) -> bool { + self.digit_separator() != 0 + } + /// The base prefix character in the packed struct. + /// + /// See [`base_prefix`][Self::base_prefix]. pub const BASE_PREFIX: u8 = 0; - /// Get the character for the base prefix. + /// Get the optional character for the base prefix. + /// + /// This character will come after a leading zero, so for example + /// setting the base prefix to `x` means that a leading `0x` will + /// be ignore, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base prefix allowed. + /// + /// # Examples + /// + /// Using a base prefix of `x`. /// - /// If the base prefix is 0, base prefixes are not allowed. - /// The number will have then have the format `0$base_prefix...`. - /// For example, a hex base prefix would be `0x`. Base prefixes are - /// always optional. + /// | Input | Valid? | + /// |:-:|:-:| + /// | `0x1` | ✔️ | + /// | `x1` | ❌ | + /// | `1` | ✔️ | + /// | `1x` | ❌ | + /// | `1x1` | ❌ | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn base_prefix(&self) -> u8 { Self::BASE_PREFIX @@ -540,14 +1148,32 @@ impl NumberFormat { } /// The base suffix character in the packed struct. + /// + /// See [`base_suffix`][Self::base_suffix]. pub const BASE_SUFFIX: u8 = 0; - /// Character for the base suffix. + /// Get the optional character for the base suffix. + /// + /// This character will at the end of the buffer, so for example + /// setting the base prefix to `x` means that a trailing `x` will + /// be ignored, if present. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix` along with + /// `format`. Defaults to `0`, or no base suffix allowed. + /// + /// # Examples + /// + /// Using a base suffix of `x`. + /// + /// | Input | Valid? | + /// |:-:|:-:| + /// | `1` | ✔️ | + /// | `1x` | ✔️ | + /// | `1d` | ❌ | + /// + /// # Used For /// - /// If not provided, base suffixes are not allowed. - /// The number will have then have the format `...$base_suffix`. - /// For example, a hex base prefix would be `0x`. Base prefixes are - /// always optional. + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn base_suffix(&self) -> u8 { Self::BASE_SUFFIX @@ -562,58 +1188,115 @@ impl NumberFormat { // RADIX /// The radix for the significant digits in the packed struct. + /// + /// See [`mantissa_radix`][Self::mantissa_radix]. pub const MANTISSA_RADIX: u32 = flags::mantissa_radix(FORMAT); - /// Get the radix for the mantissa digits. + /// Get the radix for mantissa digits. + /// + /// This is only used for the significant digits, that is, the integral and + /// fractional components. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults + /// to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "10011010010" | 1234 | + /// | 3 | "1200201" | 1234 | + /// | 8 | "2322" | 1234 | + /// | 10 | "1234" | 1234 | + /// | 16 | "4d2" | 1234 | + /// | 31 | "18p" | 1234 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer + /// - Write Float + /// - Write Integer #[inline(always)] pub const fn mantissa_radix(&self) -> u32 { Self::MANTISSA_RADIX } /// The radix for the significant digits in the packed struct. - /// Alias for `MANTISSA_RADIX`. + /// + /// Alias for [`MANTISSA_RADIX`][Self::MANTISSA_RADIX]. pub const RADIX: u32 = Self::MANTISSA_RADIX; /// Get the radix for the significant digits. + /// + /// This is an alias for [`mantissa_radix`][Self::mantissa_radix]. #[inline(always)] pub const fn radix(&self) -> u32 { Self::RADIX } - /// Get the radix**2 for the significant digits. + /// Get the `radix^2` for the significant digits. #[inline(always)] pub const fn radix2(&self) -> u32 { self.radix().wrapping_mul(self.radix()) } - /// Get the radix**4 for the significant digits. + /// Get the `radix^4` for the significant digits. #[inline(always)] pub const fn radix4(&self) -> u32 { self.radix2().wrapping_mul(self.radix2()) } - /// Get the radix*** for the significant digits. + /// Get the `radix^8` for the significant digits. #[inline(always)] pub const fn radix8(&self) -> u32 { self.radix4().wrapping_mul(self.radix4()) } /// The base for the exponent. + /// + /// See [`exponent_base`][Self::exponent_base]. pub const EXPONENT_BASE: u32 = flags::exponent_base(FORMAT); - /// Get the base for the exponent. + /// Get the radix for the exponent. + /// + /// For example, in `1.234e3`, it means `1.234 * 10^3`, and the exponent + /// base here is 10. Some programming languages, like C, support hex floats + /// with an exponent base of 2, for example `0x1.8p3`, or `1.5 * 2^3`. + /// Defaults to `10`. Can only be modified with [`feature`][crate#features] + /// `power-of-two` or `radix`. Defaults to `10`. /// - /// IE, a base of 2 means we have `mantissa * 2^exponent`. - /// If not provided, it defaults to `radix`. + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn exponent_base(&self) -> u32 { Self::EXPONENT_BASE } /// The radix for the exponent digits. + /// + /// See [`exponent_radix`][Self::exponent_radix]. pub const EXPONENT_RADIX: u32 = flags::exponent_radix(FORMAT); - /// Get the radix for the exponent digits. + /// Get the radix for exponent digits. + /// + /// This is only used for the exponent digits. We assume the radix for the + /// significant digits ([`mantissa_radix`][Self::mantissa_radix]) is + /// 10 as is the exponent base. Defaults to `10`. Can only be modified with + /// [`feature`][crate#features] `power-of-two` or `radix`. Defaults to `10`. + /// + /// | Radix | String | Number | + /// |:-:|:-:|:-:| + /// | 2 | "1.234^1100" | 1.234e9 | + /// | 3 | "1.234^110" | 1.234e9 | + /// | 8 | "1.234^14" | 1.234e9 | + /// | 10 | "1.234^12" | 1.234e9 | + /// | 16 | "1.234^c" | 1.234e9 | + /// | 31 | "1.234^c" | 1.234e9 | + /// + /// # Used For + /// + /// - Parse Float + /// - Parse Integer #[inline(always)] pub const fn exponent_radix(&self) -> u32 { Self::EXPONENT_RADIX @@ -622,12 +1305,18 @@ impl NumberFormat { // FLAGS /// Get the flags from the number format. + /// + /// This contains all the non-character and non-radix values + /// in the packed struct. #[inline(always)] pub const fn flags(&self) -> u128 { FORMAT & flags::FLAG_MASK } /// Get the interface flags from the number format. + /// + /// This contains all the flags that dictate code flows, and + /// therefore excludes logic like case-sensitive characters. #[inline(always)] pub const fn interface_flags(&self) -> u128 { FORMAT & flags::INTERFACE_FLAG_MASK @@ -640,6 +1329,9 @@ impl NumberFormat { } /// Get the exponent flags from the number format. + /// + /// This contains all the flags pertaining to exponent + /// formats, including digit separators. #[inline(always)] pub const fn exponent_flags(&self) -> u128 { FORMAT & flags::EXPONENT_FLAG_MASK @@ -683,3 +1375,28 @@ impl Default for NumberFormat { Self::new() } } + +/// Get the error type from the format. +#[inline(always)] +pub(crate) const fn format_error_impl(format: u128) -> Error { + let valid_flags = flags::REQUIRED_EXPONENT_DIGITS | flags::REQUIRED_MANTISSA_DIGITS; + if !flags::is_valid_radix(flags::mantissa_radix(format)) { + Error::InvalidMantissaRadix + } else if !flags::is_valid_radix(flags::exponent_base(format)) { + Error::InvalidExponentBase + } else if !flags::is_valid_radix(flags::exponent_radix(format)) { + Error::InvalidExponentRadix + } else if !flags::is_valid_digit_separator(format) { + Error::InvalidDigitSeparator + } else if !flags::is_valid_base_prefix(format) { + Error::InvalidBasePrefix + } else if !flags::is_valid_base_suffix(format) { + Error::InvalidBaseSuffix + } else if !flags::is_valid_punctuation(format) { + Error::InvalidPunctuation + } else if (format & flags::FLAG_MASK) != valid_flags { + Error::InvalidFlags + } else { + Error::Success + } +} diff --git a/lexical-util/src/num.rs b/lexical-util/src/num.rs index 09d4d17c..3e6d637b 100644 --- a/lexical-util/src/num.rs +++ b/lexical-util/src/num.rs @@ -16,28 +16,65 @@ use crate::f16::f16; // AS PRIMITIVE // ------------ -/// Type that can be converted to primitive with `as`. +/// Type that can be converted to [`primitive`] values with `as`. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html pub trait AsPrimitive: Copy + PartialEq + PartialOrd + Send + Sync + Sized { + /// Convert the value to a [`u8`], as if by `value as u8`. fn as_u8(self) -> u8; + + /// Convert the value to a [`u16`], as if by `value as u16`. fn as_u16(self) -> u16; + + /// Convert the value to a [`u32`], as if by `value as u32`. fn as_u32(self) -> u32; + + /// Convert the value to a [`u64`], as if by `value as u64`. fn as_u64(self) -> u64; + + /// Convert the value to a [`u128`], as if by `value as u128`. fn as_u128(self) -> u128; + + /// Convert the value to a [`usize`], as if by `value as usize`. fn as_usize(self) -> usize; + + /// Convert the value to an [`i8`], as if by `value as i8`. fn as_i8(self) -> i8; + + /// Convert the value to an [`i16`], as if by `value as i16`. fn as_i16(self) -> i16; + + /// Convert the value to an [`i32`], as if by `value as i32`. fn as_i32(self) -> i32; + + /// Convert the value to an [`i64`], as if by `value as i64`. fn as_i64(self) -> i64; + + /// Convert the value to an [`i128`], as if by `value as i128`. fn as_i128(self) -> i128; + + /// Convert the value to an [`isize`], as if by `value as isize`. fn as_isize(self) -> isize; + + /// Convert the value to an [`f32`], as if by `value as f32`. fn as_f32(self) -> f32; + + /// Convert the value to an [`f64`], as if by `value as f64`. fn as_f64(self) -> f64; + + /// Convert the value from a [`u32`], as if by `value as _`. fn from_u32(value: u32) -> Self; + + /// Convert the value from a [`u64`], as if by `value as _`. fn from_u64(value: u64) -> Self; + /// Convert the value to an [`struct@f16`], identical to `value as f16` + /// if [`struct@f16`] was a primitive type. #[cfg(feature = "f16")] fn as_f16(self) -> f16; + /// Convert the value to an [`struct@bf16`], identical to `value as bf16` + /// if [`struct@bf16`] was a primitive type. #[cfg(feature = "f16")] fn as_bf16(self) -> bf16; } @@ -247,14 +284,34 @@ half_as_primitive! { f16 bf16 } // AS CAST // ------- -/// An interface for casting between machine scalars. +/// An interface for casting between machine scalars, as if `as` was used. +/// +/// All values that the type can be cast to must be [`primitive`] values. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html pub trait AsCast: AsPrimitive { /// Creates a number from another value that can be converted into - /// a primitive via the `AsPrimitive` trait. + /// a primitive via the [`AsPrimitive`] trait. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::AsCast; + /// + /// assert_eq!(u8::as_cast(256u16), 256u16 as u8); // 0 + /// ``` fn as_cast(n: N) -> Self; } /// Allows the high-level conversion of generic types as if `as` was used. +/// +/// # Examples +/// +/// ```rust +/// use lexical_util::num::as_cast; +/// +/// assert_eq!(as_cast::(256u16), 256u16 as u8); // 0 +/// ``` #[inline(always)] pub fn as_cast(t: T) -> U { U::as_cast(t) @@ -298,7 +355,9 @@ as_cast!( // PRIMITIVE // --------- -/// Primitive type trait (which all have static lifetimes). +/// The base trait for all [`primitive`] types. +/// +/// [`primitive`]: https://doc.rust-lang.org/rust-by-example/primitives.html pub trait Primitive: 'static + fmt::Debug + fmt::Display + AsCast {} macro_rules! primitive { @@ -315,7 +374,7 @@ primitive! { f16 bf16 } // NUMBER // ------ -/// Numerical type trait. +/// The base trait for all numbers (integers and floating-point numbers). pub trait Number: Default + Primitive + @@ -331,7 +390,7 @@ pub trait Number: ops::Sub + ops::SubAssign { - /// If the number is a signed type. + /// If the number can hold negative values. const IS_SIGNED: bool; } @@ -370,7 +429,9 @@ number_impl! { // INTEGER // ------- -/// Defines a trait that supports integral operations. +/// The base trait for all signed and unsigned [`integers`]. +/// +/// [`integers`]: https://en.wikipedia.org/wiki/Integer_(computer_science) pub trait Integer: // Basic Number + Eq + Ord + @@ -389,35 +450,160 @@ pub trait Integer: ops::ShrAssign + { // CONSTANTS + /// A value equal to `0`. const ZERO: Self; + + /// A value equal to `1`. const ONE: Self; + + /// A value equal to `2`. const TWO: Self; + + /// The largest value that can be represented by this integer type. + /// + /// See [`u32::MAX`]. const MAX: Self; + + /// The smallest value that can be represented by this integer type. + /// + /// See [`u32::MIN`]. const MIN: Self; + + /// The size of this integer type in bits. + /// + /// See [`u32::BITS`]. const BITS: usize; // FUNCTIONS (INHERITED) + /// Returns the number of leading zeros in the binary representation + /// of `self`. + /// + /// See [`u32::leading_zeros`]. fn leading_zeros(self) -> u32; + + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// See [`u32::trailing_zeros`]. fn trailing_zeros(self) -> u32; + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// See [`u32::pow`]. fn pow(self, exp: u32) -> Self; + + /// Checked exponentiation. Computes `self.pow(exp)`, returning + /// `None` if overflow occurred. + /// + /// See [`u32::checked_pow`]. fn checked_pow(self, exp: u32) -> Option; + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// See [`u32::overflowing_pow`]. fn overflowing_pow(self, exp: u32) -> (Self, bool); + + /// Checked integer addition. Computes `self + i`, returning `None` if + /// overflow occurred. + /// + /// See [`u32::checked_add`]. fn checked_add(self, i: Self) -> Option; + + /// Checked integer subtraction. Computes `self - i`, returning `None` + /// if overflow occurred. + /// + /// See [`u32::checked_sub`]. fn checked_sub(self, i: Self) -> Option; + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` + /// if overflow occurred. + /// + /// See [`u32::checked_mul`]. fn checked_mul(self, i: Self) -> Option; + + /// Calculates `self + i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_add`]. fn overflowing_add(self, i: Self) -> (Self, bool); + + /// Calculates `self - i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_sub`]. fn overflowing_sub(self, i: Self) -> (Self, bool); + + /// Calculates `self * i`. + /// + /// Returns a tuple of the addition along with a boolean indicating whether + /// an arithmetic overflow would occur. If an overflow would have occurred + /// then the wrapped value is returned. See [`u32::overflowing_mul`]. fn overflowing_mul(self, i: Self) -> (Self, bool); + + /// Wrapping (modular) addition. Computes `self + i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_add`]. fn wrapping_add(self, i: Self) -> Self; + + /// Wrapping (modular) subtraction. Computes `self - i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_sub`]. fn wrapping_sub(self, i: Self) -> Self; + + /// Wrapping (modular) multiplication. Computes `self * i`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_mul`]. fn wrapping_mul(self, i: Self) -> Self; + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at + /// the boundary of the type. + /// + /// See [`u32::wrapping_neg`]. fn wrapping_neg(self) -> Self; + + /// Saturating integer addition. Computes `self + i`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_add`]. fn saturating_add(self, i: Self) -> Self; + + /// Saturating integer subtraction. Computes `self - i`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_sub`]. fn saturating_sub(self, i: Self) -> Self; + + /// Saturating integer multiplication. Computes `self * i`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// See [`u32::saturating_mul`]. fn saturating_mul(self, i: Self) -> Self; /// Get the fast ceiling of the quotient from integer division. - /// Not safe, since the remainder can easily overflow. + /// + /// The remainder may wrap to the numerical boundaries for the type. + /// See [`u32::div_ceil`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_divmod(10), (25, 0)); + /// assert_eq!(256u16.ceil_divmod(10), (26, -4)); + /// assert_eq!(i32::MAX.ceil_divmod(-2), (-0x3FFFFFFE, 3)); + /// + /// // notice how `-1` wraps since `i32` cannot hold `i128::MAX`. + /// assert_eq!((i128::MAX - 1).ceil_divmod(i128::MAX), (1, -1)); + /// ``` #[inline(always)] fn ceil_divmod(self, y: Self) -> (Self, i32) { let q = self / y; @@ -429,14 +615,41 @@ pub trait Integer: } /// Get the fast ceiling of the quotient from integer division. - /// Not safe, since the remainder can easily overflow. + /// + /// This is identical to [`u32::div_ceil`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_div(10), 25); + /// assert_eq!(256u16.ceil_div(10), 26); + /// assert_eq!(i32::MAX.ceil_div(-2), -0x3FFFFFFE); + /// assert_eq!((i128::MAX - 1).ceil_div(i128::MAX), 1); + /// ``` #[inline(always)] fn ceil_div(self, y: Self) -> Self { self.ceil_divmod(y).0 } /// Get the fast ceiling modulus from integer division. - /// Not safe, since the remainder can easily overflow. + /// + /// The remainder is not guaranteed to be valid since it can + /// overflow if the remainder is not 0. See [`Self::ceil_divmod`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(250u16.ceil_mod(10), 0); + /// assert_eq!(256u16.ceil_mod(10), -4); + /// assert_eq!(i32::MAX.ceil_mod(-2), 3); + /// + /// // notice how `-1` wraps since `i32` cannot hold `i128::MAX`. + /// assert_eq!((i128::MAX - 1).ceil_mod(i128::MAX), -1); + /// ``` #[inline(always)] fn ceil_mod(self, y: Self) -> i32 { self.ceil_divmod(y).1 @@ -445,6 +658,17 @@ pub trait Integer: // PROPERTIES /// Get the number of bits in a value. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Integer; + /// + /// assert_eq!(1u64.bit_length(), 1); + /// assert_eq!(2u64.bit_length(), 2); + /// assert_eq!(3u64.bit_length(), 2); + /// assert_eq!(16u64.bit_length(), 5); + /// ``` #[inline(always)] fn bit_length(self) -> u32 { Self::BITS as u32 - self.leading_zeros() @@ -464,7 +688,7 @@ pub trait Integer: /// Get the maximum number of digits before the slice will overflow. /// - /// This is effectively the floor(log(2**BITS-1, radix)), but we can + /// This is effectively the `floor(log(2^BITS-1, radix))`, but we can /// try to go a bit lower without worrying too much. #[inline(always)] fn overflow_digits(radix: u32) -> usize { @@ -587,7 +811,10 @@ integer_impl! { u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 usize isize } // SIGNED INTEGER // -------------- -/// Defines a trait that supports signed integral operations. +/// The trait for types that support [`signed`] integral operations, that is, +/// they can hold negative numbers. +/// +/// [`signed`]: https://en.wikipedia.org/wiki/Integer_(computer_science)#Value_and_representation pub trait SignedInteger: Integer + ops::Neg {} macro_rules! signed_integer_impl { @@ -601,7 +828,10 @@ signed_integer_impl! { i8 i16 i32 i64 i128 isize } // UNSIGNED INTEGER // ---------------- -/// Defines a trait that supports unsigned integral operations. +/// The trait for types that support [`unsigned`] integral operations, that is, +/// they can only hold positive numbers. +/// +/// [`unsigned`]: https://en.wikipedia.org/wiki/Integer_(computer_science)#Value_and_representation pub trait UnsignedInteger: Integer {} macro_rules! unsigned_integer_impl { @@ -615,32 +845,74 @@ unsigned_integer_impl! { u8 u16 u32 u64 u128 usize } // FLOAT // ----- -/// Float information for native float types. +/// The trait for floating-point [`numbers`][`floats`]. +/// +/// Floating-point numbers are numbers that may contain a fraction +/// and are stored internally as the significant digits and an +/// exponent of base 2. +/// +/// [`floats`]: https://en.wikipedia.org/wiki/Floating-point_arithmetic #[cfg(feature = "floats")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "write-floats"))))] pub trait Float: Number + ops::Neg { /// Unsigned type of the same size. type Unsigned: UnsignedInteger; // CONSTANTS + + /// A value equal to `0`. const ZERO: Self; + + /// A value equal to `1`. const ONE: Self; + + /// A value equal to `2`. const TWO: Self; + + /// Largest finite value. + /// + /// See [`f64::MAX`]. const MAX: Self; + + /// Smallest finite value. + /// + /// See [`f64::MIN`]. const MIN: Self; + + /// Infinity (`∞`). + /// + /// See [`f64::INFINITY`]. const INFINITY: Self; + + /// Negative infinity (`−∞`). + /// + /// See [`f64::NEG_INFINITY`]. const NEG_INFINITY: Self; + + /// Not a Number (NaN). + /// + /// See [`f64::NAN`]. const NAN: Self; + + /// The size of this float type in bits. + /// + /// Analogous to [`u32::BITS`]. const BITS: usize; - /// Bitmask for the sign bit. + /// Bitmask to extract the sign from the float. const SIGN_MASK: Self::Unsigned; - /// Bitmask for the exponent, including the hidden bit. + + /// Bitmask to extract the biased exponent, including the hidden bit. const EXPONENT_MASK: Self::Unsigned; - /// Bitmask for the hidden bit in exponent, which is an implicit 1 in the - /// fraction. + + /// Bitmask to extract the hidden bit in the exponent, which is an + /// implicit 1 in the significant digits. const HIDDEN_BIT_MASK: Self::Unsigned; - /// Bitmask for the mantissa (fraction), excluding the hidden bit. + + /// Bitmask to extract the mantissa (significant digits), excluding + /// the hidden bit. const MANTISSA_MASK: Self::Unsigned; + /// Mask to determine if a full-carry occurred (1 in bit above hidden bit). const CARRY_MASK: Self::Unsigned; @@ -655,36 +927,78 @@ pub trait Float: Number + ops::Neg { /// Positive infinity as bits. const INFINITY_BITS: Self::Unsigned; + /// Positive infinity as bits. const NEGATIVE_INFINITY_BITS: Self::Unsigned; - /// Size of the exponent. + + /// The number of bits in the exponent. const EXPONENT_SIZE: i32; - /// Size of the significand (mantissa) without hidden bit. + + /// Size of the significand (mantissa) without the hidden bit. const MANTISSA_SIZE: i32; - /// Bias of the exponent. + + /// Bias of the exponent. See [`exponent bias`]. + /// + /// [`exponent bias`]: https://en.wikipedia.org/wiki/Exponent_bias const EXPONENT_BIAS: i32; - /// Exponent portion of a denormal float. + + /// Exponent portion of a [`denormal`] float. + /// + /// [`denormal`]: https://en.wikipedia.org/wiki/Subnormal_number const DENORMAL_EXPONENT: i32; - /// Maximum exponent value in float. + + /// Maximum (unbiased) exponent value in the float. const MAX_EXPONENT: i32; // FUNCTIONS (INHERITED) // Re-export the to and from bits methods. + + /// Raw transmutation to the unsigned integral type. + /// + /// See [`f64::to_bits`]. fn to_bits(self) -> Self::Unsigned; + + /// Raw transmutation from the unsigned integral type. + /// + /// See [`f64::from_bits`]. fn from_bits(u: Self::Unsigned) -> Self; + + /// Returns the natural logarithm of the number. + /// + /// See [`f64::ln`]. fn ln(self) -> Self; + + /// Returns the largest integer less than or equal to `self`. + /// + /// See [`f64::floor`]. fn floor(self) -> Self; + + /// Returns true if `self` has a positive sign, including `+0.0`, + /// NaNs with positive sign bit and positive infinity. + /// + /// See [`f64::is_sign_positive`]. fn is_sign_positive(self) -> bool; + + /// Returns true if `self` has a negative sign, including `-0.0`, + /// NaNs with negative sign bit and negative infinity. + /// + /// See [`f64::is_sign_negative`]. fn is_sign_negative(self) -> bool; - /// Returns true if the float is a denormal. + /// Returns true if the float is [`denormal`]. + /// + /// Denormal (subnormal) numbers fall below the range of numbers + /// that can be stored as `mantissa * 2^exp`, and therefore + /// always have the minimum exponent. + /// + /// [`denormal`]: https://en.wikipedia.org/wiki/Subnormal_number #[inline(always)] fn is_denormal(self) -> bool { self.to_bits() & Self::EXPONENT_MASK == Self::Unsigned::ZERO } - /// Returns true if the float is a NaN or Infinite. + /// Returns true if the float is NaN, positive infinity, or negative infinity. #[inline(always)] fn is_special(self) -> bool { self.to_bits() & Self::EXPONENT_MASK == Self::EXPONENT_MASK @@ -696,7 +1010,7 @@ pub trait Float: Number + ops::Neg { self.is_special() && (self.to_bits() & Self::MANTISSA_MASK) != Self::Unsigned::ZERO } - /// Returns true if the float is infinite. + /// Returns true if the float is positive or negative infinity. #[inline(always)] fn is_inf(self) -> bool { self.is_special() && (self.to_bits() & Self::MANTISSA_MASK) == Self::Unsigned::ZERO @@ -723,7 +1037,7 @@ pub trait Float: Number + ops::Neg { self.is_sign_negative() && !self.is_nan() } - /// Get exponent component from the float. + /// Get the unbiased exponent component from the float. #[inline(always)] fn exponent(self) -> i32 { if self.is_denormal() { @@ -735,7 +1049,7 @@ pub trait Float: Number + ops::Neg { biased_e - Self::EXPONENT_BIAS } - /// Get mantissa (significand) component from float. + /// Get the mantissa (significand) component from float. #[inline(always)] fn mantissa(self) -> Self::Unsigned { let bits = self.to_bits(); @@ -748,6 +1062,16 @@ pub trait Float: Number + ops::Neg { } /// Get next greater float. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Float; + /// + /// assert_eq!(1f32.next(), 1.0000001); + /// assert_eq!((-0.0f32).next(), 0.0); // +0.0 + /// assert_eq!(0f32.next(), 1e-45); + /// ``` #[inline(always)] fn next(self) -> Self { let bits = self.to_bits(); @@ -764,6 +1088,7 @@ pub trait Float: Number + ops::Neg { } /// Get next greater float for a positive float. + /// /// Value must be >= 0.0 and < INFINITY. #[inline(always)] fn next_positive(self) -> Self { @@ -772,6 +1097,16 @@ pub trait Float: Number + ops::Neg { } /// Get previous greater float, such that `self.prev().next() == self`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_util::num::Float; + /// + /// assert_eq!(1f32.prev(), 0.99999994); + /// assert_eq!(0.0f32.prev(), 0.0); // -0.0 + /// assert_eq!((-0.0f32).prev(), -1e-45); + /// ``` #[inline(always)] fn prev(self) -> Self { let bits = self.to_bits(); @@ -806,6 +1141,14 @@ pub trait Float: Number + ops::Neg { } /// Get the max of two finite numbers. + /// + /// This assumes that both floats form a [`total ord`], + /// that is, `x < y` is always `y >= x`. Non-finite floats, + /// such as NaN, break this criteria, but finite floats enable + /// simpler (and faster) comparison criteria while remaining + /// accurate. + /// + /// [`total ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html #[inline(always)] fn max_finite(self, f: Self) -> Self { debug_assert!(!self.is_special() && !f.is_special(), "max_finite self={} f={}", self, f); @@ -817,6 +1160,14 @@ pub trait Float: Number + ops::Neg { } /// Get the min of two finite numbers. + /// + /// This assumes that both floats form a [`total ord`], + /// that is, `x < y` is always `y >= x`. Non-finite floats, + /// such as NaN, break this criteria, but finite floats enable + /// simpler (and faster) comparison criteria while remaining + /// accurate. + /// + /// [`total ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html #[inline(always)] fn min_finite(self, f: Self) -> Self { debug_assert!(!self.is_special() && !f.is_special(), "min_finite self={} f={}", self, f); diff --git a/lexical-util/src/options.rs b/lexical-util/src/options.rs index f2e67b4f..323433a3 100644 --- a/lexical-util/src/options.rs +++ b/lexical-util/src/options.rs @@ -1,4 +1,22 @@ //! Shared traits for the options API. +//! +//! The following constants have the following signifiers: +//! +//! - `${X}_LITERAL`: Applies to all literal values for that language (for +//! example, [`RUST_LITERAL`]). +//! - `${X}_STRING`: Applies to all string values for that language (for +//! example, [`ERLANG_STRING`]). +//! - `${X}`: Applies to all values for that language (for example, [`KAWA`]). +//! - `${X}_(NAN|INF|INFINITY)`: Applies to only a single special value (for +//! example, [`PHP_LITERAL_NAN`], [`PHP_LITERAL_INF`], and +//! [`PHP_LITERAL_INFINITY`]). +//! +//! If it's not defined, all values are the default. The default options +//! are: +//! - NaN: (`*_NAN`): `NaN` +//! - Short infinity: (`*_INF`): `Inf` (including `+Inf` and `-Inf`) +//! - Long infinity: (`*_INFINITY`): `Infinity` (including `+Infinity` and +//! `-Infinity`) #[cfg(feature = "write")] use crate::constants::FormattedSize; @@ -6,17 +24,72 @@ use crate::constants::FormattedSize; // TRAITS // ------ +#[doc(hidden)] +#[macro_export] +macro_rules! write_options_doc { + () => { + " +Get an upper bound on the required buffer size. + +
+ +This method is soft-deprecated and meant for internal use. +You should always use [`buffer_size_const`] so you can get +the required buffer size at compile time to determine the +buffer size required. + +
+ +[`buffer_size_const`]: Self::buffer_size_const + +This is used when custom formatting options, such as significant +digits specifiers or custom exponent breaks, are used, which +can lead to more or less significant digits being written than +expected. If using the default formatting options, then this will +always be [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] or +[`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], +depending on the radix. +" + }; +} + /// Shared trait for all writer options. +#[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "write-integers"))))] #[cfg(feature = "write")] pub trait WriteOptions: Default { /// Determine if the options are valid. fn is_valid(&self) -> bool; - /// Get an upper bound on the buffer size. + /// Get an upper bound on the required buffer size. + /// + ///
+ /// + /// This method is soft-deprecated and meant for internal use. + /// You should always use `buffer_size_const` for either [`integer`] or + /// [`float`] writer so you can get the required buffer size at compile time + /// to determine the buffer size required. + /// + ///
+ /// + /// [`integer`]: https://docs.rs/lexical-write-integer/latest/lexical_write_integer/struct.Options.html#method.buffer_size_const + /// [`float`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/struct.Options.html#method.buffer_size_const + /// + /// This is used when custom formatting options, such as significant + /// digits specifiers or custom exponent breaks, are used, which + /// can lead to more or less significant digits being written than + /// expected. If using the default formatting options, then this will + /// always be [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] or + /// [`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], + /// depending on the radix. + /// + /// Using `buffer_size_const` lets you create static arrays at compile time, + /// rather than dynamically-allocate memory or know the value ahead of time. + #[deprecated = "Use `buffer_size_const` instead. Will be removed in 2.0."] fn buffer_size(&self) -> usize; } /// Shared trait for all parser options. +#[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "write-integers"))))] #[cfg(feature = "parse")] pub trait ParseOptions: Default { /// Determine if the options are valid. @@ -34,114 +107,116 @@ pub trait ParseOptions: Default { // IF it's not defined, all values are the default. macro_rules! literal { - ($name:ident, $value:ident) => { + ($name:ident, $value:ident $(, $doc:literal)?) => { + $(#[doc = $doc])? pub const $name: Option<&[u8]> = $value; }; - ($name:ident, $value:literal) => { + ($name:ident, $value:literal $(, $doc:literal)?) => { + $(#[doc = $doc])? pub const $name: Option<&[u8]> = Some($value); }; } -literal!(RUST_LITERAL, None); +literal!(RUST_LITERAL, None, "A Rust literal number (uses default options)."); // RUST_STRING -literal!(PYTHON_LITERAL, None); +literal!(PYTHON_LITERAL, None, "A Python literal number (uses default options)."); // PYTHON_STRING -literal!(CXX_LITERAL_NAN, b"NAN"); -literal!(CXX_LITERAL_INF, b"INFINITY"); -literal!(CXX_LITERAL_INFINITY, b"INFINITY"); +literal!(CXX_LITERAL_NAN, b"NAN", "A C++ literal NaN (`NAN`)."); +literal!(CXX_LITERAL_INF, b"INFINITY", "A C++ literal short infinity (`INFINITY`)."); +literal!(CXX_LITERAL_INFINITY, b"INFINITY", "A C++ literal long infinity (`INFINITY`)."); // CXX_STRING -literal!(C_LITERAL_NAN, b"NAN"); -literal!(C_LITERAL_INF, b"INFINITY"); -literal!(C_LITERAL_INFINITY, b"INFINITY"); +literal!(C_LITERAL_NAN, b"NAN", "A C literal NaN (`NAN`)."); +literal!(C_LITERAL_INF, b"INFINITY", "A C literal short infinity (`INFINITY`)."); +literal!(C_LITERAL_INFINITY, b"INFINITY", "A C literal long infinity (`INFINITY`)."); // RUBY_LITERAL -literal!(RUBY_LITERAL_NAN, b"NaN"); -literal!(RUBY_LITERAL_INF, b"Infinity"); -literal!(RUBY_STRING_NONE, None); +literal!(RUBY_LITERAL_NAN, b"NaN", "A Ruby literal NaN (`NaN`)."); +literal!(RUBY_LITERAL_INF, b"Infinity", "A C literal short infinity (`Infinity`)."); +literal!(RUBY_STRING_NONE, None, "A Ruby string (uses default options)."); // C_STRING -literal!(SWIFT_LITERAL, None); +literal!(SWIFT_LITERAL, None, "A Swift literal number (uses default options)."); // SWIFT_STRING -literal!(GO_LITERAL, None); +literal!(GO_LITERAL, None, "A Golang literal number (uses default options)."); // GO_STRING -literal!(HASKELL_LITERAL, None); -literal!(HASKELL_STRING_INF, b"Infinity"); -literal!(HASKELL_STRING_INFINITY, b"Infinity"); -literal!(JAVASCRIPT_INF, b"Infinity"); -literal!(JAVASCRIPT_INFINITY, b"Infinity"); -literal!(PERL_LITERAL, None); +literal!(HASKELL_LITERAL, None, "A Haskell literal number (uses default options)."); +literal!(HASKELL_STRING_INF, b"Infinity", "A Haskell string short infinity (`Infinity`)."); +literal!(HASKELL_STRING_INFINITY, b"Infinity", "A Haskell string long infinity (`Infinity`)."); +literal!(JAVASCRIPT_INF, b"Infinity", "A JavaScript string short infinity (`Infinity`)."); +literal!(JAVASCRIPT_INFINITY, b"Infinity", "A JavaScript string long infinity (`Infinity`)."); +literal!(PERL_LITERAL, None, "A Perl literal literal (uses default options)."); // PERL_STRING -literal!(PHP_LITERAL_NAN, b"NAN"); -literal!(PHP_LITERAL_INF, b"INF"); -literal!(PHP_LITERAL_INFINITY, b"INF"); +literal!(PHP_LITERAL_NAN, b"NAN", "A PHP literal NaN (`NAN`)."); +literal!(PHP_LITERAL_INF, b"INF", "A PHP literal short infinity (`INF`)."); +literal!(PHP_LITERAL_INFINITY, b"INF", "A PHP literal long infinity (`INF`)."); // PHP_STRING -literal!(JAVA_LITERAL, None); -literal!(JAVA_STRING_INF, b"Infinity"); -literal!(JAVA_STRING_INFINITY, b"Infinity"); -literal!(R_LITERAL_INF, b"Inf"); -literal!(R_LITERAL_INFINITY, b"Inf"); +literal!(JAVA_LITERAL, None, "A Java literal number (uses default options)."); +literal!(JAVA_STRING_INF, b"Infinity", "A Java string short infinity (`Infinity`)."); +literal!(JAVA_STRING_INFINITY, b"Infinity", "A Java string long infinity (`Infinity`)."); +literal!(R_LITERAL_INF, b"Inf", "An R literal short infinity (`Inf`)."); +literal!(R_LITERAL_INFINITY, b"Inf", "An R literal long infinity (`Inf`)."); // R_STRING -literal!(KOTLIN_LITERAL, None); -literal!(KOTLIN_STRING_INF, b"Infinity"); -literal!(KOTLIN_STRING_INFINITY, b"Infinity"); -literal!(JULIA_LITERAL_INF, b"Inf"); -literal!(JULIA_LITERAL_INFINITY, b"Inf"); +literal!(KOTLIN_LITERAL, None, "A Kotlin literal number (uses default options)."); +literal!(KOTLIN_STRING_INF, b"Infinity", "A Kotlin string short infinity (`Infinity`)."); +literal!(KOTLIN_STRING_INFINITY, b"Infinity", "A Kotlin string long infinity (`Infinity`)."); +literal!(JULIA_LITERAL_INF, b"Inf", "A Julia string short infinity (`Inf`)."); +literal!(JULIA_LITERAL_INFINITY, b"Inf", "A Julia string long infinity (`Inf`)."); // JULIA_STRING -literal!(CSHARP_LITERAL, None); -literal!(CSHARP_STRING_INF, b"Infinity"); -literal!(CSHARP_STRING_INFINITY, b"Infinity"); -literal!(KAWA, None); -literal!(GAMBITC, None); -literal!(GUILE, None); -literal!(CLOJURE_LITERAL, None); -literal!(CLOJURE_STRING_INF, b"Infinity"); -literal!(CLOJURE_STRING_INFINITY, b"Infinity"); -literal!(ERLANG_LITERAL_NAN, b"nan"); -literal!(ERLANG_STRING, None); -literal!(ELM_LITERAL, None); -literal!(ELM_STRING_NAN, None); -literal!(ELM_STRING_INF, b"Infinity"); -literal!(ELM_STRING_INFINITY, b"Infinity"); -literal!(SCALA_LITERAL, None); -literal!(SCALA_STRING_INF, b"Infinity"); -literal!(SCALA_STRING_INFINITY, b"Infinity"); -literal!(ELIXIR, None); -literal!(FORTRAN_LITERAL, None); +literal!(CSHARP_LITERAL, None, "A C# literal number (uses default options)."); +literal!(CSHARP_STRING_INF, b"Infinity", "A C# string short infinity (`Infinity`)."); +literal!(CSHARP_STRING_INFINITY, b"Infinity", "A C# string long infinity (`Infinity`)."); +literal!(KAWA, None, "A Kawa (List) literal number (uses default options)."); +literal!(GAMBITC, None, "A Gambit-C (List) literal number (uses default options)."); +literal!(GUILE, None, "A Guile (List) literal number (uses default options)."); +literal!(CLOJURE_LITERAL, None, "A Clojure (Lisp) literal number (uses default options)."); +literal!(CLOJURE_STRING_INF, b"Infinity", "A Clojure string short infinity (`Infinity`)."); +literal!(CLOJURE_STRING_INFINITY, b"Infinity", "A Clojure string long infinity (`Infinity`)."); +literal!(ERLANG_LITERAL_NAN, b"nan", "An Erlang literal NaN (`nan`)."); +literal!(ERLANG_STRING, None, "An Erlang string number (uses default options)."); +literal!(ELM_LITERAL, None, "An Elm literal number (uses default options)."); +literal!(ELM_STRING_NAN, None, "An Elm stromg NaN (uses default options)."); +literal!(ELM_STRING_INF, b"Infinity", "An Elm string short infinity (`Infinity`)."); +literal!(ELM_STRING_INFINITY, b"Infinity", "An Elm string long infinity (`Infinity`)."); +literal!(SCALA_LITERAL, None, "A Scala literal number (uses default options)."); +literal!(SCALA_STRING_INF, b"Infinity", "A Scala string short infinity (`Infinity`)."); +literal!(SCALA_STRING_INFINITY, b"Infinity", "A Scala string long infinity (`Infinity`)."); +literal!(ELIXIR, None, "An Elixir number (uses default options)."); +literal!(FORTRAN_LITERAL, None, "A FORTRAN literal number (uses default options)."); // FORTRAN_STRING -literal!(D_LITERAL, None); +literal!(D_LITERAL, None, "A D-Lang literal number (uses default options)."); // D_STRING -literal!(COFFEESCRIPT_INF, b"Infinity"); -literal!(COFFEESCRIPT_INFINITY, b"Infinity"); -literal!(COBOL, None); -literal!(FSHARP_LITERAL_NAN, b"nan"); -literal!(FSHARP_LITERAL_INF, b"infinity"); -literal!(FSHARP_LITERAL_INFINITY, b"infinity"); +literal!(COFFEESCRIPT_INF, b"Infinity", "A CoffeeScript string short infinity (`Infinity`)."); +literal!(COFFEESCRIPT_INFINITY, b"Infinity", "A CoffeeScript string long infinity (`Infinity`)."); +literal!(COBOL, None, "A COBOL literal number (uses default options)."); +literal!(FSHARP_LITERAL_NAN, b"nan", "An F# literal NaN (`nan`)."); +literal!(FSHARP_LITERAL_INF, b"infinity", "An F# literal short infinity (`infinity`)."); +literal!(FSHARP_LITERAL_INFINITY, b"infinity", "An F# literal long infinity (`infinity`)."); // FSHARP_STRING -literal!(VB_LITERAL, None); -literal!(VB_STRING_INF, None); -literal!(VB_STRING_INFINITY, None); -literal!(OCAML_LITERAL_NAN, b"nan"); -literal!(OCAML_LITERAL_INF, b"infinity"); -literal!(OCAML_LITERAL_INFINITY, b"infinity"); +literal!(VB_LITERAL, None, "A Visual Basic literal number (uses default options)"); +literal!(VB_STRING_INF, None, "A Visual Basic short string infinity (uses default options)"); +literal!(VB_STRING_INFINITY, None, "A Visual Basic long string number (uses default options)"); +literal!(OCAML_LITERAL_NAN, b"nan", "An OCAML literal NaN (`nan`)."); +literal!(OCAML_LITERAL_INF, b"infinity", "An OCAML literal short infinity (`infinity`)."); +literal!(OCAML_LITERAL_INFINITY, b"infinity", "An OCAML literal long infinity (`infinity`)."); // OCAML_STRING -literal!(OBJECTIVEC, None); -literal!(REASONML_LITERAL_NAN, b"nan"); -literal!(REASONML_LITERAL_INF, b"infinity"); -literal!(REASONML_LITERAL_INFINITY, b"infinity"); +literal!(OBJECTIVEC, None, "An Objective-C number (uses default options)."); +literal!(REASONML_LITERAL_NAN, b"nan", "A ReasonML literal NaN (`nan`)."); +literal!(REASONML_LITERAL_INF, b"infinity", "A ReasonML literal short infinity (`infinity`)."); +literal!(REASONML_LITERAL_INFINITY, b"infinity", "A ReasonML literal long infinity (`infinity`)."); // REASONML_STRING -literal!(MATLAB_LITERAL_INF, b"inf"); -literal!(MATLAB_LITERAL_INFINITY, b"Inf"); +literal!(MATLAB_LITERAL_INF, b"inf", "A MATLAB literal short infinity (`inf`)."); +literal!(MATLAB_LITERAL_INFINITY, b"Inf", "A MATLAB literal long infinity (`Inf`)."); // MATLAB_STRING -literal!(ZIG_LITERAL, None); +literal!(ZIG_LITERAL, None, "A Zig literal number (uses default options)."); // ZIG_STRING -literal!(SAGE_LITERAL_INF, b"infinity"); -literal!(SAGE_LITERAL_INFINITY, b"Infinity"); +literal!(SAGE_LITERAL_INF, b"infinity", "A SageMath literal short infinity (`infinity`)."); +literal!(SAGE_LITERAL_INFINITY, b"Infinity", "A SageMath literal long infinity (`Infinity`)."); // SAGE_STRING -literal!(JSON, None); -literal!(TOML, None); -literal!(YAML, None); -literal!(XML_INF, None); -literal!(XML_INFINITY, None); -literal!(SQLITE, None); -literal!(POSTGRESQL, None); -literal!(MYSQL, None); -literal!(MONGODB_INF, b"Infinity"); -literal!(MONGODB_INFINITY, b"Infinity"); +literal!(JSON, None, "A JSON number (uses default options)."); +literal!(TOML, None, "A TOML number (uses default options)."); +literal!(YAML, None, "A YAML number (uses default options)."); +literal!(XML_INF, None, "An XML short infinity (uses default options)."); +literal!(XML_INFINITY, None, "An XML short infinity (uses default options)."); +literal!(SQLITE, None, "A SQLite number (uses default options)."); +literal!(POSTGRESQL, None, "A PostgreSQL number (uses default options)."); +literal!(MYSQL, None, "A MySQL number (uses default options)."); +literal!(MONGODB_INF, b"Infinity", "A MongoDB short infinity (`Infinity`)."); +literal!(MONGODB_INFINITY, b"Infinity", "A MongoDB long infinity (`Infinity`)."); diff --git a/lexical-util/src/result.rs b/lexical-util/src/result.rs index e9350dbd..44fbe28e 100644 --- a/lexical-util/src/result.rs +++ b/lexical-util/src/result.rs @@ -4,5 +4,5 @@ use core::result; use crate::error; -/// A specialized Result type for lexical operations. +/// A specialized [`Result`][`result::Result`] type for lexical operations. pub type Result = result::Result; diff --git a/lexical-util/src/step.rs b/lexical-util/src/step.rs index 8b852802..0db79890 100644 --- a/lexical-util/src/step.rs +++ b/lexical-util/src/step.rs @@ -1,5 +1,5 @@ //! The maximum digits that can be held in a u64 for a given radix without -//! overflow. +//! overflowing. //! //! This is useful for 128-bit division and operations, since it can //! reduces the number of inefficient, non-native operations. @@ -17,11 +17,15 @@ // Fallback radixes use 1 for the value to avoid infinite loops, // but allowing them in `const fn`. -/// Get the maximum number of digits that can be processed without overflowing. +/// Get the number of digits that can be always processed without overflowing. +/// +/// Calculate the number of digits that can always be processed without +/// overflowing for a given type, that is, it can process every (positive) value +/// in the range `[0, radix^N)` where `N` is the number of digits. /// -/// Calculate the maximum number of digits that can always be processed -/// without overflowing for a given type. For example, 19 digits can -/// always be processed for a decimal string for `u64` without overflowing. +/// For example, with [`u8`] with radix `10`, we have a maximum value of `255`, +/// so we have a min step of `2`: that is, we can always process values from +/// `[0, 10^2)` (or `[0, 100)`). #[inline(always)] #[allow(clippy::needless_return)] // reason="required depending on our radix configuration" pub const fn min_step(radix: u32, bits: usize, is_signed: bool) -> usize { @@ -91,10 +95,13 @@ pub const fn min_step(radix: u32, bits: usize, is_signed: bool) -> usize { /// Get the maximum number of digits that can be processed without overflowing. /// -/// Calculate the maximum number of digits that can be processed -/// without always overflowing for a given type. For example, 20 digits can -/// be processed for a decimal string for `u64` without overflowing, but -/// it may overflow. +/// Calculate the number of digits that can be processed without overflowing for +/// a given type, that is, it can process at least `radix^N`. This does not +/// necessarily mean it can process every value in the range `[0, radix^(N+1))`. +/// +/// For example, with [`u8`] with radix `10`, we have a maximum value of `255`, +/// so we have a max step of `3`: that is, it can process up to 3 digits (`[100, +/// 256)`), even if it **cannot** process every 3 digit value (`[256, 1000)`). #[inline(always)] #[allow(clippy::needless_return)] // reason="required depending on our radix configuration" pub const fn max_step(radix: u32, bits: usize, is_signed: bool) -> usize { @@ -161,8 +168,10 @@ pub const fn max_step(radix: u32, bits: usize, is_signed: bool) -> usize { } /// Calculate the number of digits that can be processed without overflowing a -/// u64. Helper function since this is used for 128-bit division. -#[inline(always)] +/// [`u64`]. Helper function since this is used for 128-bit division. +/// +/// This is an alias for [`min_step`] with `bits == 64` and `is_signed == +/// false`. pub const fn u64_step(radix: u32) -> usize { min_step(radix, 64, false) } diff --git a/lexical-util/tests/feature_format_tests.rs b/lexical-util/tests/feature_format_tests.rs index 858b9341..ead3fc53 100644 --- a/lexical-util/tests/feature_format_tests.rs +++ b/lexical-util/tests/feature_format_tests.rs @@ -63,7 +63,7 @@ macro_rules! test_flag { const FORMAT: u128 = format::NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .$field(true) - .build(); + .build_unchecked(); let fmt = format::NumberFormat:: {}; assert_eq!(fmt.is_valid(), true); assert_eq!(fmt.$field(), true); @@ -74,7 +74,7 @@ macro_rules! test_flag { .leading_digit_separator(true) .trailing_digit_separator(true) .$field(true) - .build(); + .build_unchecked(); let fmt = format::NumberFormat:: {}; assert_eq!(fmt.is_valid(), true); assert_eq!(fmt.$field(), true); diff --git a/lexical-util/tests/format_flags_tests.rs b/lexical-util/tests/format_flags_tests.rs index d4e1429b..039f81c0 100644 --- a/lexical-util/tests/format_flags_tests.rs +++ b/lexical-util/tests/format_flags_tests.rs @@ -9,7 +9,7 @@ const fn from_digit_separator(digit_separator: u8) -> u128 { format::NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(digit_separator)) .digit_separator_flags(true) - .build() + .build_unchecked() } #[cfg(feature = "format")] @@ -31,8 +31,9 @@ fn test_is_valid_digit_separator() { // Try with a custom radix. #[cfg(feature = "radix")] { - let format = - format::NumberFormat::<{ from_digit_separator(b'e') }>::rebuild().radix(16).build(); + let format = format::NumberFormat::<{ from_digit_separator(b'e') }>::rebuild() + .radix(16) + .build_unchecked(); assert_eq!(format::is_valid_digit_separator(format), false); } } @@ -44,7 +45,7 @@ fn is_valid_punctuation(digit_separator: u8, base_prefix: u8, base_suffix: u8) - .digit_separator_flags(true) .base_prefix(num::NonZeroU8::new(base_prefix)) .base_suffix(num::NonZeroU8::new(base_suffix)) - .build(); + .build_unchecked(); format::is_valid_punctuation(fmt) } diff --git a/lexical-util/tests/iterator_tests.rs b/lexical-util/tests/iterator_tests.rs index 46fe0df4..1cf0c9de 100644 --- a/lexical-util/tests/iterator_tests.rs +++ b/lexical-util/tests/iterator_tests.rs @@ -61,14 +61,12 @@ fn digits_iterator_test() { fn skip_iterator_test() { use core::num; - use lexical_util::format::{NumberFormat, NumberFormatBuilder}; - use static_assertions::const_assert; + use lexical_util::format::NumberFormatBuilder; pub const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .digit_separator_flags(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); type Digits<'a> = Bytes<'a, { FORMAT }>; assert!(!Digits::IS_CONTIGUOUS); diff --git a/lexical-util/tests/skip_tests.rs b/lexical-util/tests/skip_tests.rs index f3e88bf2..158e3421 100644 --- a/lexical-util/tests/skip_tests.rs +++ b/lexical-util/tests/skip_tests.rs @@ -2,9 +2,8 @@ use core::num; -use lexical_util::format::{NumberFormat, NumberFormatBuilder}; +use lexical_util::format::NumberFormatBuilder; use lexical_util::iterator::AsBytes; -use static_assertions::const_assert; fn skip_iter_eq(input: &[u8], output: &[u8]) { // next is done in terms of peek, so we're safe here. @@ -19,8 +18,7 @@ fn test_skip_iter_i() { pub const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -60,8 +58,7 @@ fn test_skip_iter_l() { pub const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .integer_leading_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -101,8 +98,7 @@ fn test_skip_iter_t() { pub const FORMAT: u128 = NumberFormatBuilder::new() .digit_separator(num::NonZeroU8::new(b'_')) .integer_trailing_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -143,8 +139,7 @@ fn test_skip_iter_il() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .integer_leading_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -185,8 +180,7 @@ fn test_skip_iter_it() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .integer_trailing_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -227,8 +221,7 @@ fn test_skip_iter_lt() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_leading_digit_separator(true) .integer_trailing_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -270,8 +263,7 @@ fn test_skip_iter_ilt() { .integer_internal_digit_separator(true) .integer_leading_digit_separator(true) .integer_trailing_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -312,8 +304,7 @@ fn test_skip_iter_ic() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_internal_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -354,8 +345,7 @@ fn test_skip_iter_lc() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_leading_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -396,8 +386,7 @@ fn test_skip_iter_tc() { .digit_separator(num::NonZeroU8::new(b'_')) .integer_trailing_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -439,8 +428,7 @@ fn test_skip_iter_ilc() { .integer_internal_digit_separator(true) .integer_leading_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -482,8 +470,7 @@ fn test_skip_iter_itc() { .integer_internal_digit_separator(true) .integer_trailing_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -525,8 +512,7 @@ fn test_skip_iter_ltc() { .integer_leading_digit_separator(true) .integer_trailing_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); @@ -569,8 +555,7 @@ fn test_skip_iter_iltc() { .integer_leading_digit_separator(true) .integer_trailing_digit_separator(true) .integer_consecutive_digit_separator(true) - .build(); - const_assert!(NumberFormat::<{ FORMAT }> {}.is_valid()); + .build_strict(); skip_iter_eq::<{ FORMAT }>(b"123.45", b"123.45"); skip_iter_eq::<{ FORMAT }>(b"1e45", b"1e45"); diff --git a/lexical-write-float/Cargo.toml b/lexical-write-float/Cargo.toml index 219d9d37..1004c9af 100644 --- a/lexical-write-float/Cargo.toml +++ b/lexical-write-float/Cargo.toml @@ -30,9 +30,6 @@ path = "../lexical-write-integer" default-features = false features = [] -[dependencies] -static_assertions = "1" - [features] default = ["std"] # Use the standard library. @@ -79,3 +76,4 @@ f128 = ["lexical-util/f128"] [package.metadata.docs.rs] features = ["radix", "format", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-write-float/src/api.rs b/lexical-write-float/src/api.rs index 0513990e..56b03d49 100644 --- a/lexical-write-float/src/api.rs +++ b/lexical-write-float/src/api.rs @@ -45,8 +45,8 @@ macro_rules! float_to_lexical { )*) } -to_lexical! {} -to_lexical_with_options! {} +to_lexical!("lexical_write_float", 1.234, f64); +to_lexical_with_options!("lexical_write_float", 1.234, f64, Options); float_to_lexical! { f32 ; f64 ; diff --git a/lexical-write-float/src/lib.rs b/lexical-write-float/src/lib.rs index c5860791..99a3fbbf 100644 --- a/lexical-write-float/src/lib.rs +++ b/lexical-write-float/src/lib.rs @@ -1,13 +1,410 @@ //! Fast and compact float-to-string conversions. //! -//! # Features +//! This contains high-performance methods to write floating-point numbers +//! directly to bytes, can be converted to [`str`] using +//! [`str::from_utf8`]. Using [`to_lexical`] is analogous to [`to_string`], +//! just writing to an existing buffer. +//! +//! It also contains extensively formatting control, including the use of +//! exponent notation, if to round or truncate floats, the number of significant +//! digits, and more. +//! +//! [`str::from_utf8`]: core::str::from_utf8 +//! [`to_lexical`]: ToLexical::to_lexical +//! +//! # Getting Started +//! +//! To write a number to bytes, use [`to_lexical`]: +//! +//! [`to_lexical`]: ToLexical::to_lexical +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_write_float::{FormattedSize, ToLexical}; +//! +//! let mut buffer = [0u8; f64::FORMATTED_SIZE_DECIMAL]; +//! let digits = 1.234f64.to_lexical(&mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("1.234")); +//! ``` +//! +//! With the default options, using [`FORMATTED_SIZE_DECIMAL`] +//! guarantees the buffer will be large enough to write the digits for all +//! numbers of that type. +//! +//! [`FORMATTED_SIZE_DECIMAL`]: FormattedSize::FORMATTED_SIZE_DECIMAL +//! +//! # Options/Formatting API //! //! Each float formatter contains extensive formatting control, including //! a maximum number of significant digits written, a minimum number of //! significant digits remaining, the positive and negative exponent break //! points (at what exponent, in scientific-notation, to force scientific -//! notation), whether to force or disable scientific notation, and the -//! rounding mode for truncated float strings. +//! notation), whether to force or disable scientific notation, the rounding +//! mode for truncated float strings, and how to display non-finite floats. +//! While using custom float options, you must use +//! [`Options::buffer_size_const`] to determine the correct buffer size: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! # use core::str; +//! use lexical_write_float::{format, options, ToLexicalWithOptions}; +//! +//! const BUFFER_SIZE: usize = options::RUST_LITERAL +//! .buffer_size_const::(); +//! +//! fn write_rust_float(f: f64) -> ([u8; BUFFER_SIZE], usize) { +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! let digits = f.to_lexical_with_options::<{ format::RUST_LITERAL }>( +//! &mut buffer, +//! &options::RUST_LITERAL +//! ); +//! let count = digits.len(); +//! (buffer, count) +//! } +//! +//! let (digits, count) = write_rust_float(3.5); +//! assert_eq!(str::from_utf8(&digits[..count]), Ok("3.5")); +//! # } +//! ``` +//! +//! For additional supported options for customizing how to write floats, see +//! the [`OptionsBuilder`]. If you're looking to parse floats with a grammar +//! for a programming language, many pre-defined options such as for [`JSON`] +//! exist in [`mod@options`]. For even more customization, see the +//! [`format`](#format) and [Comprehensive Configuration] sections +//! below. +//! +//! [`JSON`]: crate::options::JSON +//! [Comprehensive Configuration]: #comprehensive-configuration +//! +//! # Features +//! +//! * `format` - Add support for custom float formatting. +//! * `power-of-two` - Add support for writing power-of-two float strings. +//! * `radix` - Add support for strings of any radix. +//! * `compact` - Reduce code size at the cost of performance. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. +//! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format +//! +//! A complete description of supported features includes: +//! +//! #### format +//! +//! Add support custom float formatting specifications. This should be used in +//! conjunction with [`Options`] for extensible float writing. You must use +//! [`Options::buffer_size_const`] to determine the number of bytes requires in +//! the buffer. This allows changing the use of exponent notation, requiring or +//! not allowing signs, and more. +//! +//! ##### JSON +//! +//! For example, in JSON, the following floats are valid or invalid: +//! +//! ```text +//! -1 // valid +//! +1 // invalid +//! 1 // valid +//! 1. // invalid +//! .1 // invalid +//! 0.1 // valid +//! nan // invalid +//! inf // invalid +//! Infinity // invalid +//! ``` +//! +//! All of the finite numbers are valid in Rust, and Rust supports non-finite +//! floats. In order to write standard-conforming JSON floats using +//! `lexical-core`, you may use the following approach: +//! +//! ```rust +//! # #[cfg(feature = "format")] { +//! # use core::str; +//! use lexical_write_float::{format, options, ToLexicalWithOptions}; +//! +//! const BUFFER_SIZE: usize = options::JSON.buffer_size_const::(); +//! +//! fn write_json_float(f: f64) -> ([u8; BUFFER_SIZE], usize) { +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! let digits = f.to_lexical_with_options::<{ format::JSON }>( +//! &mut buffer, +//! &options::JSON +//! ); +//! let count = digits.len(); +//! (buffer, count) +//! } +//! +//! let (digits, count) = write_json_float(3.5); +//! assert_eq!(str::from_utf8(&digits[..count]), Ok("3.5")); +//! # } +//! ``` +//! +//! ##### Custom Signs +//! +//! An example of building a custom format to ensure positive signs are always +//! written is as follows: +//! +//! ```rust +//! # #[cfg(feature = "radix")] { +//! # use core::str; +//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::new() +//! // require a `+` or `-` sign before the number +//! .required_mantissa_sign(true) +//! // require a `+` or `-` sign before the exponent digits +//! .required_exponent_sign(true) +//! // build the format, panicking if the format is invalid +//! .build_strict(); +//! const OPTIONS: Options = Options::new(); +//! +//! const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::(); +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! +//! let digits = 1.234e300f64.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("+1.234e+300")); +//! # } +//! ``` +//! +//! Enabling the [`format`](crate#format) API significantly increases compile +//! times, however, it enables a large amount of customization in how floats are +//! written. +//! +//! #### power-of-two +//! +//! Enable writing numbers that are powers of two, that is, `2`, `4`, `8`, `16`, +//! and `32`. In these cases, you should use [`FORMATTED_SIZE`] to create a +//! sufficiently large buffer. +//! +//! [`FORMATTED_SIZE`]: FormattedSize::FORMATTED_SIZE +//! +//! ```rust +//! # #[cfg(feature = "power-of-two")] { +//! # use core::str; +//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions}; +//! +//! let mut buffer = [0u8; f64::FORMATTED_SIZE]; +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! const OPTIONS: Options = Options::new(); +//! let digits = 1.234f64.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("1.0011101111100111011011001000101101000011100101011")); +//! # } +//! ``` +//! +//! #### radix +//! +//! Enable writing numbers using all radixes from `2` to `36`. This requires +//! more static storage than [`power-of-two`][crate#power-of-two], and increases +//! compile times, but can be quite useful for esoteric programming languages +//! which use duodecimal floats, for example. +//! +//! ```rust +//! # #[cfg(feature = "radix")] { +//! # use core::str; +//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions}; +//! +//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12); +//! const OPTIONS: Options = Options::new(); +//! +//! let mut buffer = [0u8; f64::FORMATTED_SIZE]; +//! let digits = 1.234f64.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("1.29842830A44BAA2")); +//! # } +//! ``` +//! +//! #### compact +//! +//! Reduce the generated code size at the cost of performance. This minimizes +//! the number of static tables, inlining, and generics used, drastically +//! reducing the size of the generated binaries. However, this resulting +//! performance of the generated code is much lower. +//! +//! #### f16 +//! +//! This enables the use of the half-precision floats [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`]. However, since these have limited hardware support +//! and are primarily used for vectorized operations, they are formatted as if +//! they were an [`f32`]. Due to the low precision of 16-bit floats, the results +//! may appear to have significant rounding error. +//! +//! ```rust +//! # #[cfg(feature = "f16")] { +//! # use core::str; +//! use lexical_write_float::{f16, FormattedSize, ToLexical}; +//! +//! let mut buffer = [0u8; f16::FORMATTED_SIZE]; +//! let value = f16::from_f64_const(1.234f64); +//! let digits = value.to_lexical(&mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("1.234375")); +//! # } +//! ``` +//! +//! #### std +//! +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. +//! +//! # Comprehensive Configuration +//! +//! `lexical-write-float` provides two main levels of configuration: +//! - The [`NumberFormatBuilder`], creating a packed struct with custom +//! formatting options. +//! - The [`Options`] API. +//! +//! ## Number Format +//! +//! The number format class provides numerous flags to specify number writing. +//! When the [`power-of-two`](#power-of-two) feature is enabled, additional +//! flags are added: +//! - The radix for the significant digits (default `10`). +//! - The radix for the exponent base (default `10`). +//! - The radix for the exponent digits (default `10`). +//! +//! When the [`format`](#format) feature is enabled, numerous other syntax and +//! digit separator flags are enabled, including: +//! - Requiring or ommitting `+` signs. +//! - If to use exponent notation. +//! +//! Many pre-defined constants therefore exist to simplify common use-cases, +//! including: +//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, +//! and many more. +//! +//! For a list of all supported fields, see [Write +//! Float Fields][NumberFormatBuilder#write-float-fields]. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`JSON`]: format::JSON +[`XML`]: format::XML +[`TOML`]: format::TOML +[`YAML`]: format::YAML +[`SQLite`]: format::SQLITE +[`Rust`]: format::RUST_LITERAL +[`Python`]: format::PYTHON_LITERAL +[`C#`]: format::CSHARP_LITERAL +[`FORTRAN`]: format::FORTRAN_LITERAL +[`COBOL`]: format::COBOL_LITERAL +" +)] +#![cfg_attr( + not(feature = "format"), + doc = " +[`JSON`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.COBOL_LITERAL.html +" +)] +//! +//! ## Options API +//! +//! The Options API provides high-level options to specify number parsing +//! or writing, options not intrinsically tied to a number format. +//! For example, the Options API provides: +//! - The [`exponent`][Options::exponent] character (defaults to `b'e'` or +//! `b'^'`, depending on the radix). +//! - The [`decimal point`][Options::decimal_point] character (defaults to +//! `b'.'`). +//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string +//! [`representations`][Options::nan_string]. +//! - Whether to [`trim`][Options::trim_floats] the fraction component from +//! integral floats. +//! - The exponent [`break-point`][Options::positive_exponent_break] for +//! scientific notation. +//! - The [`maximum`][Options::max_significant_digits] and +//! [`minimum`][Options::min_significant_digits] number of significant digits +//! to write. +//! - The rounding [`mode`][Options::round_mode] when truncating significant +//! digits while writing. +//! +//! In addition, pre-defined constants for each category of options may +//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`]. +//! +//! [`JSON-OPTS`]: options::JSON +//! +//! ## Examples +//! +//! An example of creating your own options to parse European-style +//! numbers (which use commas as decimal points, controlling the number +//! of significant digits, special number representations, and more, is as +//! follows: +//! +//! ```rust +//! # use core::{num, str}; +//! use lexical_write_float::{FormattedSize, Options, ToLexicalWithOptions}; +//! +//! const FORMAT: u128 = lexical_write_float::format::STANDARD; +//! const CUSTOM: Options = Options::builder() +//! // write exponents as "1.2^10" and not "1.2e10" +//! .exponent(b'^') +//! // use the European decimal point, so "1,2" and not "1.2" +//! .decimal_point(b',') +//! // write NaN and Infinity using the following formats +//! .nan_string(Some(b"nan")) +//! .inf_string(Some(b"inf")) +//! // set the minimum and maximum number of significant digits to write; +//! .min_significant_digits(num::NonZeroUsize::new(3)) +//! .max_significant_digits(num::NonZeroUsize::new(5)) +//! .build_strict(); +//! +//! const BUFFER_SIZE: usize = CUSTOM.buffer_size_const::(); +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! +//! // write 4 digits, no exponent notation +//! let digits = 1.234f64.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("1,234")); +//! +//! // write 6 digits, rounding to 5 +//! let digits = 1.23456f64.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("1,2346")); +//! +//! // write 6 digits, rounding to 5, with exponent notation +//! let digits = 1.23456e300f64.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("1,2346^300")); +//! +//! // write 4 digits, no exponent notation +//! let digits = 1.2f64.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("1,20")); +//! +//! // write a literal NaN string +//! let digits = f64::NAN.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("nan")); +//! +//! // write a literal +Infinity string +//! let digits = f64::INFINITY.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("inf")); +//! ``` +//! +//! # Higher-Level APIs +//! +//! If you would like support for writing to [`String`] directly, use +//! [`lexical`] instead. If you would like an API that supports multiple numeric +//! conversions rather than just writing integers, use [`lexical-core`] instead. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. //! //! # Algorithms //! @@ -30,54 +427,23 @@ //! The radix algorithm is adapted from the V8 codebase, and may be found //! [here](https://github.com/v8/v8). //! -//! # Features -//! -//! * `std` - Use the standard library. -//! * `power-of-two` - Add support for wring power-of-two float strings. -//! * `radix` - Add support for strings of any radix. -//! * `compact` - Reduce code size at the cost of performance. -//! * `safe` - Ensure only memory-safe indexing is used. -//! -//! # Note -//! -//! Only documented functionality is considered part of the public API: -//! any of the modules, internal functions, or structs may change -//! release-to-release without major or minor version changes. Use -//! internal implementation details at your own risk. -//! -//! lexical-write-float mainly exists as an implementation detail for -//! lexical-core, although its API is stable. If you would like to use -//! a high-level API that writes to and parses from `String` and `&str`, -//! respectively, please look at [lexical](https://crates.io/crates/lexical) -//! instead. If you would like an API that supports multiple numeric -//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) -//! instead. -//! -//! # Version Support -//! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. -//! -//! # Safety -//! -//! The `unsafe` usage in the options does not actually have any actual safety -//! concerns unless the format is not validated: the float formatters assume -//! NaN and Infinite strings are <= 50 characters. Creating custom format -//! specification with longer special strings and not validating the : -//! incorrectly using the API however can cause incorrect floating -//! point specifications due to conflicting requirements. -//! //! # Design //! //! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) //! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Benchmarks.md) //! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html +//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html +//! [`to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -126,10 +492,12 @@ mod table_grisu; #[cfg(feature = "f16")] pub use lexical_util::bf16::bf16; pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; +pub use lexical_util::error::Error; #[cfg(feature = "f16")] pub use lexical_util::f16::f16; -pub use lexical_util::format::{self, NumberFormatBuilder}; +pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder}; pub use lexical_util::options::WriteOptions; +pub use lexical_util::result::Result; pub use self::api::{ToLexical, ToLexicalWithOptions}; #[doc(inline)] diff --git a/lexical-write-float/src/options.rs b/lexical-write-float/src/options.rs index afda918c..b0d71cf3 100644 --- a/lexical-write-float/src/options.rs +++ b/lexical-write-float/src/options.rs @@ -1,6 +1,203 @@ //! Configuration options for writing floats. - -use core::{mem, num}; +//! +//! This enables extensive control over how the float is written, from +//! control characters like the decimal point, to the use of exponent +//! notation, and the number of significant digits. +//! +//! # Examples +//! +//! For example, to customize the writing of numbers for new exponent +//! and decimal point characters, you would use [`Options`] to create +//! a custom format and write the integer using the format. +//! +//! ```rust +//! # use core::{num, str}; +//! use lexical_write_float::{FormattedSize, Options, ToLexicalWithOptions}; +//! use lexical_write_float::format::STANDARD; +//! +//! let value = 1.234e45f64; +//! +//! const CUSTOM: Options = Options::builder() +//! // write exponents as "1.2^10" and not "1.2e10" +//! .exponent(b'^') +//! // use the European decimal point, so "1,2" and not "1.2" +//! .decimal_point(b',') +//! .build_strict(); +//! +//! const BUFFER_SIZE: usize = CUSTOM.buffer_size_const::(); +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! let digits = value.to_lexical_with_options::(&mut buffer, &CUSTOM); +//! assert_eq!(str::from_utf8(digits), Ok("1,234^45")); +//! ``` +//! +//! # Pre-Defined Formats +//! +//! These are the pre-defined formats for parsing numbers from various +//! programming, markup, and data languages. +//! +//! - [`STANDARD`]: Standard number format. +//! - [`DECIMAL_COMMA`]: Numerical format with a decimal comma. +//! - [`HEX_FLOAT`]: Numerical format for hexadecimal floats, which use a `p` +//! exponent. +//! - [`CARAT_EXPONENT`]: Numerical format where `^` is used as the exponent +//! notation character. +//! - [`RUST_LITERAL`]: Number format for a [`Rust`] literal floating-point +//! number. +//! - [`PYTHON_LITERAL`]: Number format for a [`Python`] literal floating-point +//! number. +//! - [`CXX_LITERAL`]: Number format for a [`C++`] literal floating-point +//! number. +//! - [`C_LITERAL`]: Number format for a [`C`] literal floating-point number. +//! - [`RUBY_LITERAL`]: Number format for a [`Ruby`] literal floating-point +//! number. +//! - [`RUBY_STRING`]: Number format to parse a [`Ruby`] float from string. +//! - [`SWIFT_LITERAL`]: Number format for a [`Swift`] literal floating-point +//! number. +//! - [`GO_LITERAL`]: Number format for a [`Golang`] literal floating-point +//! number. +//! - [`HASKELL_LITERAL`]: Number format for a [`Haskell`] literal +//! floating-point number. +//! - [`HASKELL_STRING`]: Number format to parse a [`Haskell`] float from +//! string. +//! - [`JAVASCRIPT_LITERAL`]: Number format for a [`Javascript`] literal +//! floating-point number. +//! - [`JAVASCRIPT_STRING`]: Number format to parse a [`Javascript`] float from +//! string. +//! - [`PERL_LITERAL`]: Number format for a [`Perl`] literal floating-point +//! number. +//! - [`PHP_LITERAL`]: Number format for a [`PHP`] literal floating-point +//! number. +//! - [`JAVA_LITERAL`]: Number format for a [`Java`] literal floating-point +//! number. +//! - [`JAVA_STRING`]: Number format to parse a [`Java`] float from string. +//! - [`R_LITERAL`]: Number format for an [`R`] literal floating-point number. +//! - [`KOTLIN_LITERAL`]: Number format for a [`Kotlin`] literal floating-point +//! number. +//! - [`KOTLIN_STRING`]: Number format to parse a [`Kotlin`] float from string. +//! - [`JULIA_LITERAL`]: Number format for a [`Julia`] literal floating-point +//! number. +//! - [`CSHARP_LITERAL`]: Number format for a [`C#`] literal floating-point +//! number. +//! - [`CSHARP_STRING`]: Number format to parse a [`C#`] float from string. +//! - [`KAWA_LITERAL`]: Number format for a [`Kawa`] literal floating-point +//! number. +//! - [`KAWA_STRING`]: Number format to parse a [`Kawa`] float from string. +//! - [`GAMBITC_LITERAL`]: Number format for a [`Gambit-C`] literal +//! floating-point number. +//! - [`GAMBITC_STRING`]: Number format to parse a [`Gambit-C`] float from +//! string. +//! - [`GUILE_LITERAL`]: Number format for a [`Guile`] literal floating-point +//! number. +//! - [`GUILE_STRING`]: Number format to parse a [`Guile`] float from string. +//! - [`CLOJURE_LITERAL`]: Number format for a [`Clojure`] literal +//! floating-point number. +//! - [`CLOJURE_STRING`]: Number format to parse a [`Clojure`] float from +//! string. +//! - [`ERLANG_LITERAL`]: Number format for an [`Erlang`] literal floating-point +//! number. +//! - [`ERLANG_STRING`]: Number format to parse an [`Erlang`] float from string. +//! - [`ELM_LITERAL`]: Number format for an [`Elm`] literal floating-point +//! number. +//! - [`ELM_STRING`]: Number format to parse an [`Elm`] float from string. +//! - [`SCALA_LITERAL`]: Number format for a [`Scala`] literal floating-point +//! number. +//! - [`SCALA_STRING`]: Number format to parse a [`Scala`] float from string. +//! - [`ELIXIR_LITERAL`]: Number format for an [`Elixir`] literal floating-point +//! number. +//! - [`ELIXIR_STRING`]: Number format to parse an [`Elixir`] float from string. +//! - [`FORTRAN_LITERAL`]: Number format for a [`FORTRAN`] literal +//! floating-point number. +//! - [`D_LITERAL`]: Number format for a [`D`] literal floating-point number. +//! - [`COFFEESCRIPT_LITERAL`]: Number format for a [`Coffeescript`] literal +//! floating-point number. +//! - [`COFFEESCRIPT_STRING`]: Number format to parse a [`Coffeescript`] float +//! from string. +//! - [`COBOL_LITERAL`]: Number format for a [`COBOL`] literal floating-point +//! number. +//! - [`COBOL_STRING`]: Number format to parse a [`COBOL`] float from string. +//! - [`FSHARP_LITERAL`]: Number format for an [`F#`] literal floating-point +//! number. +//! - [`VB_LITERAL`]: Number format for a [`Visual Basic`] literal +//! floating-point number. +//! - [`VB_STRING`]: Number format to parse a [`Visual Basic`] float from +//! string. +//! - [`OCAML_LITERAL`]: Number format for an [`OCaml`] literal floating-point +//! number. +//! - [`OBJECTIVEC_LITERAL`]: Number format for an [`Objective-C`] literal +//! floating-point number. +//! - [`OBJECTIVEC_STRING`]: Number format to parse an [`Objective-C`] float +//! from string. +//! - [`REASONML_LITERAL`]: Number format for an [`ReasonML`] literal +//! floating-point number. +//! - [`MATLAB_LITERAL`]: Number format for a [`MATLAB`] literal floating-point +//! number. +//! - [`ZIG_LITERAL`]: Number format for a [`Zig`] literal floating-point +//! number. +//! - [`SAGE_LITERAL`]: Number format for a [`Sage`] literal floating-point +//! number. +//! - [`JSON`]: Number format for a [`JSON`][`JSON-REF`] literal floating-point +//! number. +//! - [`TOML`]: Number format for a [`TOML`][`TOML-REF`] literal floating-point +//! number. +//! - [`YAML`]: Number format for a [`YAML`][`YAML-REF`] literal floating-point +//! number. +//! - [`XML`]: Number format for an [`XML`][`XML-REF`] literal floating-point +//! number. +//! - [`SQLITE`]: Number format for a [`SQLite`] literal floating-point number. +//! - [`POSTGRESQL`]: Number format for a [`PostgreSQL`] literal floating-point +//! number. +//! - [`MYSQL`]: Number format for a [`MySQL`] literal floating-point number. +//! - [`MONGODB`]: Number format for a [`MongoDB`] literal floating-point +//! number. +//! +//! +//! +//! [`Rust`]: https://www.rust-lang.org/ +//! [`Python`]: https://www.python.org/ +//! [`C++`]: https://en.cppreference.com/w/ +//! [`C`]: https://en.cppreference.com/w/c +//! [`Ruby`]: https://www.ruby-lang.org/en/ +//! [`Swift`]: https://developer.apple.com/swift/ +//! [`Golang`]: https://go.dev/ +//! [`Haskell`]: https://www.haskell.org/ +//! [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript +//! [`Perl`]: https://www.perl.org/ +//! [`PHP`]: https://www.php.net/ +//! [`Java`]: https://www.java.com/en/ +//! [`R`]: https://www.r-project.org/ +//! [`Kotlin`]: https://kotlinlang.org/ +//! [`Julia`]: https://julialang.org/ +//! [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ +//! [`Kawa`]: https://www.gnu.org/software/kawa/ +//! [`Gambit-C`]: https://gambitscheme.org/ +//! [`Guile`]: https://www.gnu.org/software/guile/ +//! [`Clojure`]: https://clojure.org/ +//! [`Erlang`]: https://www.erlang.org/ +//! [`Elm`]: https://elm-lang.org/ +//! [`Scala`]: https://www.scala-lang.org/ +//! [`Elixir`]: https://elixir-lang.org/ +//! [`FORTRAN`]: https://fortran-lang.org/ +//! [`D`]: https://dlang.org/ +//! [`Coffeescript`]: https://coffeescript.org/ +//! [`Cobol`]: https://www.ibm.com/think/topics/cobol +//! [`F#`]: https://fsharp.org/ +//! [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ +//! [`OCaml`]: https://ocaml.org/ +//! [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C +//! [`ReasonML`]: https://reasonml.github.io/ +//! [`Matlab`]: https://www.mathworks.com/products/matlab.html +//! [`Zig`]: https://ziglang.org/ +//! [`Sage`]: https://www.sagemath.org/ +//! [`JSON-REF`]: https://www.json.org/json-en.html +//! [`TOML-REF`]: https://toml.io/en/ +//! [`YAML-REF`]: https://yaml.org/ +//! [`XML-REF`]: https://en.wikipedia.org/wiki/XML +//! [`SQLite`]: https://www.sqlite.org/ +//! [`PostgreSQL`]: https://www.postgresql.org/ +//! [`MySQL`]: https://www.mysql.com/ +//! [`MongoDB`]: https://www.mongodb.com/ + +use core::num; use lexical_util::ascii::{is_valid_ascii, is_valid_letter_slice}; use lexical_util::constants::FormattedSize; @@ -8,63 +205,141 @@ use lexical_util::error::Error; use lexical_util::format::NumberFormat; use lexical_util::options::{self, WriteOptions}; use lexical_util::result::Result; -use static_assertions::const_assert; + +// NOTE: Rust guarantees the sizes are the same: +// https://doc.rust-lang.org/std/num/struct.NonZero.html /// Type with the exact same size as a `usize`. +#[doc(hidden)] pub type OptionUsize = Option; /// Type with the exact same size as a `i32`. +#[doc(hidden)] pub type OptionI32 = Option; -// Ensure the sizes are identical. -const_assert!(mem::size_of::() == mem::size_of::()); -const_assert!(mem::size_of::() == mem::size_of::()); +/// Const evaluation of `max` for integers. +macro_rules! max { + ($x:expr, $y:expr) => {{ + let x = $x; + let y = $y; + if x >= y { + x + } else { + y + } + }}; +} + +/// Const evaluation of `min` for integers. +macro_rules! min { + ($x:expr, $y:expr) => {{ + let x = $x; + let y = $y; + if x <= y { + x + } else { + y + } + }}; +} /// Enumeration for how to round floats with precision control. +/// +/// For example, using [`Round`][RoundMode::Round], `1.2345` rounded +/// to 4 digits would be `1.235`, while [`Truncate`][RoundMode::Truncate] +/// would be `1.234`. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum RoundMode { /// Round to the nearest float string with the given number of significant /// digits. Round, + /// Truncate the float string with the given number of significant digits. Truncate, } /// Maximum length for a special string. pub const MAX_SPECIAL_STRING_LENGTH: usize = 50; -const_assert!(MAX_SPECIAL_STRING_LENGTH < f32::FORMATTED_SIZE_DECIMAL); -/// Builder for `Options`. +/// Builder for [`Options`]. +/// +/// This enables extensive control over how the float is written, from +/// control characters like the decimal point, to the use of exponent +/// notation, and the number of significant digits. +/// +/// # Examples +/// +/// For example, to customize the writing of numbers for new exponent +/// and decimal point characters, you would use: +/// +/// ```rust +/// # use core::{num, str}; +/// use lexical_write_float::{FormattedSize, Options, ToLexicalWithOptions}; +/// use lexical_write_float::format::STANDARD; +/// +/// let value = 1.234e45f64; +/// +/// const CUSTOM: Options = Options::builder() +/// // write exponents as "1.2^10" and not "1.2e10" +/// .exponent(b'^') +/// // use the European decimal point, so "1,2" and not "1.2" +/// .decimal_point(b',') +/// .build_strict(); +/// +/// const BUFFER_SIZE: usize = CUSTOM.buffer_size_const::(); +/// let mut buffer = [0u8; BUFFER_SIZE]; +/// let digits = value.to_lexical_with_options::(&mut buffer, &CUSTOM); +/// assert_eq!(str::from_utf8(digits), Ok("1,234^45")); +/// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct OptionsBuilder { /// Maximum number of significant digits to write. + /// /// If not set, it defaults to the algorithm's default. max_significant_digits: OptionUsize, + /// Minimum number of significant digits to write. + /// /// If not set, it defaults to the algorithm's default. /// Note that this isn't fully respected: if you wish to format /// `0.1` with 25 significant digits, the correct result **should** /// be `0.100000000000000005551115`. However, we would output /// `0.100000000000000000000000`, which is still the nearest float. min_significant_digits: OptionUsize, + /// Maximum exponent prior to using scientific notation. + /// /// This is ignored if the exponent base is not the same as the mantissa /// radix. If not provided, use the algorithm's default. positive_exponent_break: OptionI32, + /// Minimum exponent prior to using scientific notation. + /// /// This is ignored if the exponent base is not the same as the mantissa /// radix. If not provided, use the algorithm's default. negative_exponent_break: OptionI32, + /// Rounding mode for writing digits with precision control. round_mode: RoundMode, + /// Trim the trailing ".0" from integral float strings. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. + /// + /// [`min_significant_digits`]: Self::min_significant_digits trim_floats: bool, + /// Character to designate the exponent component of a float. exponent: u8, + /// Character to separate the integer from the fraction components. decimal_point: u8, + /// String representation of Not A Number, aka `NaN`. nan_string: Option<&'static [u8]>, + /// String representation of `Infinity`. inf_string: Option<&'static [u8]>, } @@ -91,68 +366,253 @@ impl OptionsBuilder { // GETTERS /// Get the maximum number of significant digits to write. + /// + /// This limits the total number of written digits, truncating based + /// on the [`round_mode`] if more digits would normally be written. If + /// no value is provided, then it writes as many digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_max_significant_digits(), None); + /// ``` + /// + /// [`round_mode`]: Self::round_mode #[inline(always)] pub const fn get_max_significant_digits(&self) -> OptionUsize { self.max_significant_digits } /// Get the minimum number of significant digits to write. + /// + /// If more digits exist, such as writing "1.2" with a minimum of 5 + /// significant digits, then `0`s are appended to the end of the digits. If + /// no value is provided, then it writes as few digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_min_significant_digits(), None); + /// ``` #[inline(always)] pub const fn get_min_significant_digits(&self) -> OptionUsize { self.min_significant_digits } /// Get the maximum exponent prior to using scientific notation. + /// + /// If the value is set to `300`, then any value with magnitude `>= 1e300` + /// (for base 10) will be writen in exponent notation, while any lower + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `9`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_positive_exponent_break(), None); + /// ``` #[inline(always)] pub const fn get_positive_exponent_break(&self) -> OptionI32 { self.positive_exponent_break } /// Get the minimum exponent prior to using scientific notation. + /// + /// If the value is set to `-300`, then any value with magnitude `< 1e-300` + /// (for base 10) will be writen in exponent notation, while any larger + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `-5`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_negative_exponent_break(), None); + /// ``` #[inline(always)] pub const fn get_negative_exponent_break(&self) -> OptionI32 { self.negative_exponent_break } /// Get the rounding mode for writing digits with precision control. + /// + /// For example, writing `1.23456` with 5 significant digits with + /// [`RoundMode::Round`] would produce `"1.2346"` while + /// [`RoundMode::Truncate`] would produce `"1.2345"`. Defaults to + /// [`RoundMode::Round`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::{Options, RoundMode}; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_round_mode(), RoundMode::Round); + /// ``` #[inline(always)] pub const fn get_round_mode(&self) -> RoundMode { self.round_mode } - /// Get if we should trim a trailing `".0"` from floats. + /// Get if we should trim a trailing `".0"` from integral floats. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. Defaults to [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_trim_floats(), false); + /// ``` + /// + /// [`min_significant_digits`]: Self::min_significant_digits #[inline(always)] pub const fn get_trim_floats(&self) -> bool { self.trim_floats } /// Get the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_exponent(), b'e'); + /// ``` #[inline(always)] pub const fn get_exponent(&self) -> u8 { self.exponent } /// Get the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_decimal_point(), b'.'); + /// ``` #[inline(always)] pub const fn get_decimal_point(&self) -> u8 { self.decimal_point } /// Get the string representation for `NaN`. + /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`NaN`][f64::NAN] leads to an error. Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_nan_string(), Some("NaN".as_bytes())); + /// ``` #[inline(always)] pub const fn get_nan_string(&self) -> Option<&'static [u8]> { self.nan_string } - /// Get the short string representation for `Infinity`. + /// Get the string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_inf_string(), Some("inf".as_bytes())); + /// ``` #[inline(always)] pub const fn get_inf_string(&self) -> Option<&'static [u8]> { self.inf_string } + /// Get the string representation for `Infinity`. Alias for + /// [`get_inf_string`]. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// [`get_inf_string`]: Self::get_inf_string + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder(); + /// assert_eq!(builder.get_infinity_string(), Some("inf".as_bytes())); + /// ``` + #[inline(always)] + pub const fn get_infinity_string(&self) -> Option<&'static [u8]> { + self.inf_string + } + // SETTERS /// Set the maximum number of significant digits to write. + /// + /// This limits the total number of written digits, truncating based + /// on the [`round_mode`] if more digits would normally be written. If + /// no value is provided, then it writes as many digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroUsize; + /// + /// use lexical_write_float::Options; + /// + /// let max_digits = NonZeroUsize::new(300); + /// let builder = Options::builder() + /// .max_significant_digits(max_digits); + /// assert_eq!(builder.get_max_significant_digits(), max_digits); + /// ``` + /// + /// # Panics + /// + /// This will panic when building the options or writing the float if the + /// value is smaller than [`min_significant_digits`]. + /// + /// [`round_mode`]: Self::round_mode + /// [`min_significant_digits`]: Self::min_significant_digits #[inline(always)] pub const fn max_significant_digits(mut self, max_significant_digits: OptionUsize) -> Self { self.max_significant_digits = max_significant_digits; @@ -160,6 +620,31 @@ impl OptionsBuilder { } /// Set the minimum number of significant digits to write. + /// + /// If more digits exist, such as writing "1.2" with a minimum of 5 + /// significant digits, then `0`s are appended to the end of the digits. + /// If no value is provided, then it writes as few digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroUsize; + /// + /// use lexical_write_float::Options; + /// + /// let min_digits = NonZeroUsize::new(10); + /// let builder = Options::builder() + /// .min_significant_digits(min_digits); + /// assert_eq!(builder.get_min_significant_digits(), min_digits); + /// ``` + /// + /// # Panics + /// + /// This will panic when building the options or writing the float if the + /// value is larger than [`max_significant_digits`]. + /// + /// [`max_significant_digits`]: Self::max_significant_digits #[inline(always)] pub const fn min_significant_digits(mut self, min_significant_digits: OptionUsize) -> Self { self.min_significant_digits = min_significant_digits; @@ -167,6 +652,28 @@ impl OptionsBuilder { } /// Set the maximum exponent prior to using scientific notation. + /// + /// If the value is set to `300`, then any value with magnitude `>= 1e300` + /// (for base 10) will be writen in exponent notation, while any lower + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `9`. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroI32; + /// + /// use lexical_write_float::Options; + /// + /// let pos_break = NonZeroI32::new(3); + /// let builder = Options::builder() + /// .positive_exponent_break(pos_break); + /// assert_eq!(builder.get_positive_exponent_break(), pos_break); + /// ``` + /// + /// # Panics + /// + /// Panics if the value is `<= 0`. #[inline(always)] pub const fn positive_exponent_break(mut self, positive_exponent_break: OptionI32) -> Self { self.positive_exponent_break = positive_exponent_break; @@ -174,6 +681,28 @@ impl OptionsBuilder { } /// Set the minimum exponent prior to using scientific notation. + /// + /// If the value is set to `-300`, then any value with magnitude `< 1e-300` + /// (for base 10) will be writen in exponent notation, while any larger + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `-5`. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroI32; + /// + /// use lexical_write_float::Options; + /// + /// let neg_break = NonZeroI32::new(-3); + /// let builder = Options::builder() + /// .negative_exponent_break(neg_break); + /// assert_eq!(builder.get_negative_exponent_break(), neg_break); + /// ``` + /// + /// # Panics + /// + /// Panics if the value is `>= 0`. #[inline(always)] pub const fn negative_exponent_break(mut self, negative_exponent_break: OptionI32) -> Self { self.negative_exponent_break = negative_exponent_break; @@ -181,13 +710,73 @@ impl OptionsBuilder { } /// Set the rounding mode for writing digits with precision control. + /// + /// For example, writing `1.23456` with 5 significant digits with + /// [`RoundMode::Round`] would produce `"1.2346"` while + /// [`RoundMode::Truncate`] would produce `"1.2345"`. Defaults to + /// [`RoundMode::Round`]. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(all(feature = "format", feature = "radix"))] { + /// use core::{num, str}; + /// + /// use lexical_write_float::{RoundMode, Options, ToLexicalWithOptions}; + /// use lexical_write_float::format::STANDARD; + /// + /// let value = 1.23456f64; + /// + /// // truncating + /// const TRUNCATE: Options = Options::builder() + /// // truncate numbers when writing less digits than present, rather than round + /// .round_mode(RoundMode::Truncate) + /// // the maximum number of significant digits to write + /// .max_significant_digits(num::NonZeroUsize::new(5)) + /// // build the options, panicking if they're invalid + /// .build_strict(); + /// const TRUNCATE_SIZE: usize = TRUNCATE.buffer_size_const::(); + /// let mut buffer = [0u8; TRUNCATE_SIZE]; + /// let digits = value.to_lexical_with_options::(&mut buffer, &TRUNCATE); + /// assert_eq!(str::from_utf8(digits), Ok("1.2345")); + /// + /// // rounding + /// const ROUND: Options = Options::builder() + /// // round to the nearest number when writing less digits than present + /// .round_mode(RoundMode::Round) + /// // the maximum number of significant digits to write + /// .max_significant_digits(num::NonZeroUsize::new(5)) + /// // build the options, panicking if they're invalid + /// .build_strict(); + /// const ROUND_SIZE: usize = ROUND.buffer_size_const::(); + /// let mut buffer = [0u8; ROUND_SIZE]; + /// let digits = value.to_lexical_with_options::(&mut buffer, &ROUND); + /// assert_eq!(str::from_utf8(digits), Ok("1.2346")); + /// # } + /// ``` #[inline(always)] pub const fn round_mode(mut self, round_mode: RoundMode) -> Self { self.round_mode = round_mode; self } - /// Set if we should trim a trailing `".0"` from floats. + /// Set if we should trim a trailing `".0"` from integral floats. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. Defaults to [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .trim_floats(true); + /// assert_eq!(builder.get_trim_floats(), true); + /// ``` + /// + /// [`min_significant_digits`]: Self::min_significant_digits #[inline(always)] pub const fn trim_floats(mut self, trim_floats: bool) -> Self { self.trim_floats = trim_floats; @@ -195,6 +784,19 @@ impl OptionsBuilder { } /// Set the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .exponent(b'^'); + /// assert_eq!(builder.get_exponent(), b'^'); + /// ``` #[inline(always)] pub const fn exponent(mut self, exponent: u8) -> Self { self.exponent = exponent; @@ -202,6 +804,19 @@ impl OptionsBuilder { } /// Set the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .decimal_point(b'^'); + /// assert_eq!(builder.get_decimal_point(), b'^'); + /// ``` #[inline(always)] pub const fn decimal_point(mut self, decimal_point: u8) -> Self { self.decimal_point = decimal_point; @@ -210,12 +825,29 @@ impl OptionsBuilder { /// Set the string representation for `NaN`. /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`NaN`][f64::NAN] returns an error. Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .nan_string(Some(b"nan")); + /// assert_eq!(builder.get_nan_string(), Some(b"nan".as_ref())); + /// ``` + /// /// Panics /// - /// Setting a value too large may cause a panic even if [`FORMATTED_SIZE`] - /// elements are provided. + /// Setting a value with more than 50 elements will panic at runtime. You + /// should always build the format using [`build_strict`] or checking + /// [`is_valid`] prior to using the format, to avoid unexpected panics. /// /// [`FORMATTED_SIZE`]: `lexical_util::constants::FormattedSize::FORMATTED_SIZE` + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn nan_string(mut self, nan_string: Option<&'static [u8]>) -> Self { self.nan_string = nan_string; @@ -224,21 +856,71 @@ impl OptionsBuilder { /// Set the string representation for `Infinity`. /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .inf_string(Some(b"infinity")); + /// assert_eq!(builder.get_inf_string(), Some(b"infinity".as_ref())); + /// ``` + /// /// Panics /// - /// Setting a value too large may cause a panic even if [`FORMATTED_SIZE`] - /// elements are provided. + /// Setting a value with more than 50 elements will panic at runtime. You + /// should always build the format using [`build_strict`] or checking + /// [`is_valid`] prior to using the format, to avoid unexpected panics. /// /// [`FORMATTED_SIZE`]: `lexical_util::constants::FormattedSize::FORMATTED_SIZE` + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn inf_string(mut self, inf_string: Option<&'static [u8]>) -> Self { self.inf_string = inf_string; self } + /// Set the string representation for `Infinity`. Alias for [`inf_string`]. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// [`inf_string`]: Self::inf_string + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// let builder = Options::builder() + /// .infinity_string(Some(b"infinity")); + /// assert_eq!(builder.get_infinity_string(), Some(b"infinity".as_ref())); + /// ``` + /// + /// Panics + /// + /// Setting a value with more than 50 elements will panic at runtime. You + /// should always build the format using [`build_strict`] or checking + /// [`is_valid`] prior to using the format, to avoid unexpected panics. + /// + /// [`FORMATTED_SIZE`]: `lexical_util::constants::FormattedSize::FORMATTED_SIZE` + /// [`build_strict`]: Self::build_strict + /// [`is_valid`]: Self::is_valid + #[inline(always)] + pub const fn infinity_string(self, inf_string: Option<&'static [u8]>) -> Self { + self.inf_string(inf_string) + } + // BUILDERS - /// Determine if `nan_str` is valid. + /// Determine if [`nan_string`][`Self::nan_string`] is valid. + #[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else, clippy::needless_bool)] // reason="more logical" pub const fn nan_str_is_valid(&self) -> bool { @@ -259,7 +941,8 @@ impl OptionsBuilder { } } - /// Determine if `inf_str` is valid. + /// Determine if [`inf_string`][`Self::inf_string`] is valid. + #[doc(hidden)] #[inline(always)] #[allow(clippy::if_same_then_else, clippy::needless_bool)] // reason="more logical" pub const fn inf_str_is_valid(&self) -> bool { @@ -297,14 +980,20 @@ impl OptionsBuilder { } } - /// Build the Options struct without validation. + /// Build the [`Options`] struct without validation. /// - /// # Panics + ///
/// /// This is completely safe, however, misusing this, especially - /// the `nan_string` and `inf_string` representations could cause - /// panics at runtime. Always use [`MAX_SPECIAL_STRING_LENGTH`] and - /// check if [`Self::is_valid`] prior to using a created format string. + /// the [`nan_string`] and [`inf_string`] representations could cause + /// panics at runtime. Always use [`is_valid`] prior to using the built + /// options. + /// + ///
+ /// + /// [`inf_string`]: Self::inf_string + /// [`nan_string`]: Self::nan_string + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn build_unchecked(&self) -> Options { Options { @@ -321,7 +1010,23 @@ impl OptionsBuilder { } } - /// Build the Options struct. + /// Build the [`Options`] struct, panicking if the builder is invalid. + /// + /// # Panics + /// + /// If the built options are not valid. + #[inline(always)] + pub const fn build_strict(&self) -> Options { + match self.build() { + Ok(value) => value, + Err(error) => core::panic!("{}", error.description()), + } + } + + /// Build the [`Options`] struct. + /// + /// If the format is not valid, than an error is returned, + /// otherwise, the successful value is returned. #[inline(always)] #[allow(clippy::if_same_then_else)] // reason="more logical" pub const fn build(&self) -> Result { @@ -374,47 +1079,79 @@ impl Default for OptionsBuilder { /// Options to customize writing floats. /// +/// This enables extensive control over how the float is written, from +/// control characters like the decimal point, to the use of exponent +/// notation, and the number of significant digits. +/// /// # Examples /// +/// Writing a simple float with custom special strings and +/// formatting integral floats as integers can be done as: +/// /// ```rust -/// # extern crate lexical_write_float; -/// use lexical_write_float::Options; +/// use core::str; +/// +/// use lexical_write_float::{Options, ToLexical, ToLexicalWithOptions}; +/// use lexical_write_float::format::STANDARD; /// -/// # pub fn main() { -/// let options = Options::builder() +/// const OPTS: Options = Options::builder() /// .trim_floats(true) /// .nan_string(Some(b"NaN")) /// .inf_string(Some(b"Inf")) -/// .build() -/// .unwrap(); -/// # } +/// .build_strict(); +/// +/// const SIZE: usize = OPTS.buffer_size_const::(); +/// let mut buffer = [0u8; SIZE]; +/// +/// // trim floats +/// let digits = 12345.0f64.to_lexical_with_options::(&mut buffer, &OPTS); +/// assert_eq!(str::from_utf8(digits), Ok("12345")); +/// +/// // don't trim floats +/// let digits = 12345.0f64.to_lexical(&mut buffer); +/// assert_eq!(str::from_utf8(digits), Ok("12345.0")); /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct Options { /// Maximum number of significant digits to write. /// If not set, it defaults to the algorithm's default. max_significant_digits: OptionUsize, + /// Minimum number of significant digits to write. /// If not set, it defaults to the algorithm's default. min_significant_digits: OptionUsize, + /// Maximum exponent prior to using scientific notation. /// This is ignored if the exponent base is not the same as the mantissa /// radix. If not provided, use the algorithm's default. positive_exponent_break: OptionI32, + /// Minimum exponent prior to using scientific notation. /// This is ignored if the exponent base is not the same as the mantissa /// radix. If not provided, use the algorithm's default. negative_exponent_break: OptionI32, + /// Rounding mode for writing digits with precision control. round_mode: RoundMode, + /// Trim the trailing ".0" from integral float strings. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. + /// + /// [`min_significant_digits`]: Self::min_significant_digits trim_floats: bool, + /// Character to designate the exponent component of a float. exponent: u8, + /// Character to separate the integer from the fraction components. decimal_point: u8, + /// String representation of Not A Number, aka `NaN`. nan_string: Option<&'static [u8]>, + /// String representation of `Infinity`. inf_string: Option<&'static [u8]>, } @@ -429,8 +1166,18 @@ impl Options { } /// Create the default options for a given radix. + /// + ///
+ /// + /// This function will never fail even if the radix is invalid. It is up to + /// the caller to ensure the format is valid using + /// [`Options::is_valid`]. Only radixes from `2` to `36` should be used. + /// + ///
#[inline(always)] #[cfg(feature = "power-of-two")] + #[cfg_attr(docsrs, doc(cfg(feature = "power-of-two")))] + // FIXME: When we release a major version, validate the radix. pub const fn from_radix(radix: u8) -> Self { // Need to determine the correct exponent character ('e' or '^'), // since the default character is `e` normally, but this is a valid @@ -442,6 +1189,166 @@ impl Options { builder.build_unchecked() } + /// Get an upper bound on the required buffer size. + /// + /// This is used when custom formatting options, such as significant + /// digits specifiers or custom exponent breaks, are used, which + /// can lead to more or less significant digits being written than + /// expected. If using the default formatting options, then this will + /// always be [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] or + /// [`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], + /// depending on the radix. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(all(feature = "format", feature = "radix"))] { + /// use core::{num, str}; + /// + /// use lexical_write_float::{FormattedSize, Options, ToLexicalWithOptions}; + /// use lexical_write_float::format::STANDARD; + /// + /// const DEFAULT: Options = Options::builder() + /// // require at least 3 significant digits, so `0.01` would be `"0.0100"`. + /// .min_significant_digits(num::NonZeroUsize::new(3)) + /// // allow at most 5 significant digits, so `1.23456` would be `"1.2346"`. + /// .max_significant_digits(num::NonZeroUsize::new(5)) + /// // build our format, erroring if it's invalid + /// .build_strict(); + /// assert_eq!(DEFAULT.buffer_size_const::(), f64::FORMATTED_SIZE_DECIMAL); + /// + /// const CUSTOM: Options = Options::builder() + /// // require at least 300 significant digits. + /// .min_significant_digits(num::NonZeroUsize::new(300)) + /// // allow at most 500 significant digits. + /// .max_significant_digits(num::NonZeroUsize::new(500)) + /// // only write values with magnitude above 1e300 in exponent notation + /// .positive_exponent_break(num::NonZeroI32::new(300)) + /// // only write values with magnitude below 1e-300 in exponent notation + /// .negative_exponent_break(num::NonZeroI32::new(-300)) + /// .build_strict(); + /// // 300 for the significant digits (500 is never reachable), 300 extra + /// // due to the exponent breakoff, 1 for the sign, 1 for the decimal point + /// // in all cases, this is enough including the exponent character and sign. + /// assert_eq!(CUSTOM.buffer_size_const::(), 602); + /// + /// // now, write out value to bytes + /// const SIZE: usize = CUSTOM.buffer_size_const::(); + /// let mut buffer = [0u8; SIZE]; + /// let value = 1.23456e-299f64; + /// let digits = value.to_lexical_with_options::(&mut buffer, &CUSTOM); + /// + /// // validate our printed digits. 600! + /// assert_eq!(digits.len(), 600); + /// assert!(!digits.contains(&b'e') && !digits.contains(&b'E')); + /// assert!(digits.starts_with(b"0.000000000000000000000000")); + /// + /// // validate the round-trip + /// assert_eq!(str::from_utf8(digits).unwrap().parse::(), Ok(value)); + /// + /// // let's serialize a slightly smaller value + /// let value = 1.23456e-301f64; + /// let digits = value.to_lexical_with_options::(&mut buffer, &CUSTOM); + /// assert_eq!(digits.len(), 306); + /// let digits = value.to_lexical_with_options::(&mut buffer, &CUSTOM); + /// # } + /// ``` + #[inline(always)] + pub const fn buffer_size_const(&self) -> usize { + let format = NumberFormat::<{ FORMAT }> {}; + + // NOTE: This looks like it's off by 2 but it's not. We have only 2 as a + // baseline for the mantissa sign and decimal point, but we don't + // hard-code in 2 for the exponent sign and character. But we consider + // those cases already when the exponent breakof >= 5 (13 for radix). + // Say we have `1.2345612345612346e-300` for a breakoff of `-300` with + // 300 min significant digits, which would be: + // - "0." (integral + decimal point) + // - "0" * 299 (leading zeros for e-300) + // - "123456" * 50 (300 significant digits) + // + // This is exactly 601 characters, with which a sign bit is 602. + // If we go any lower, we have `1.2e-301`, which then would become + // `1.23456...e-301`, or would be 306 characters. + + // quick optimizations if no custom formatting options are used + // we ensure that the mantissa and exponent radix are the same. + let formatted_size = if format.radix() == 10 { + T::FORMATTED_SIZE_DECIMAL + } else { + T::FORMATTED_SIZE + }; + + // At least 2 for the decimal point and sign. + let mut count: usize = 2; + + // First need to calculate maximum number of digits from leading or + // trailing zeros, IE, the exponent break. + if !format.no_exponent_notation() { + let min_exp = match self.negative_exponent_break() { + Some(v) => v.get(), + None => -5, + }; + let max_exp = match self.positive_exponent_break() { + Some(v) => v.get(), + None => 9, + }; + let exp = max!(min_exp.abs(), max_exp) as usize; + if cfg!(feature = "power-of-two") && exp < 13 { + // 11 for the exponent digits in binary, 1 for the sign, 1 for the symbol + count += 13; + } else if exp < 5 { + // 3 for the exponent digits in decimal, 1 for the sign, 1 for the symbol + count += 5; + } else { + // More leading or trailing zeros than the exponent digits. + count += exp; + } + } else if cfg!(feature = "power-of-two") { + // Min is 2^-1075. + count += 1075; + } else { + // Min is 10^-324. + count += 324; + } + + // Now add the number of significant digits. + let radix = format.radix(); + let formatted_digits = if radix == 10 { + // Really should be 18, but add some extra to be cautious. + 28 + } else { + // BINARY: + // 53 significant mantissa bits for binary, add a few extra. + // RADIX: + // Our limit is `delta`. The maximum relative delta is 2.22e-16, + // around 1. If we have values below 1, our delta is smaller, but + // the max fraction is also a lot smaller. Above, and our fraction + // must be < 1.0, so our delta is less significant. Therefore, + // if our fraction is just less than 1, for a float near 2.0, + // we can do at **maximum** 33 digits (for base 3). Let's just + // assume it's a lot higher, and go with 64. + 64 + }; + let digits = if let Some(max_digits) = self.max_significant_digits() { + min!(formatted_digits, max_digits.get()) + } else { + formatted_digits + }; + let digits = if let Some(min_digits) = self.min_significant_digits() { + max!(digits, min_digits.get()) + } else { + digits + }; + count += digits; + + // we need to make sure we have at least enough room for the + // default formatting size, no matter what, just as a precaution. + count = max!(count, formatted_size); + + count + } + // GETTERS /// Check if the options state is valid. @@ -451,99 +1358,338 @@ impl Options { } /// Get the maximum number of significant digits to write. + /// + /// This limits the total number of written digits, truncating based + /// on the [`round_mode`] if more digits would normally be written. If + /// no value is provided, then it writes as many digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroUsize; + /// + /// use lexical_write_float::Options; + /// + /// const MAX_DIGITS: Option = NonZeroUsize::new(300); + /// const OPTIONS: Options = Options::builder() + /// .max_significant_digits(MAX_DIGITS) + /// .build_strict(); + /// assert_eq!(OPTIONS.max_significant_digits(), MAX_DIGITS); + /// ``` + /// + /// [`round_mode`]: Self::round_mode #[inline(always)] pub const fn max_significant_digits(&self) -> OptionUsize { self.max_significant_digits } /// Get the minimum number of significant digits to write. + /// + /// If more digits exist, such as writing "1.2" with a minimum of 5 + /// significant digits, then `0`s are appended to the end of the digits. + /// If no value is provided, then it writes as few digits as required to + /// create an unambiguous representation of the float. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroUsize; + /// + /// use lexical_write_float::Options; + /// + /// const MIN_DIGITS: Option = NonZeroUsize::new(10); + /// const OPTIONS: Options = Options::builder() + /// .min_significant_digits(MIN_DIGITS) + /// .build_strict(); + /// assert_eq!(OPTIONS.min_significant_digits(), MIN_DIGITS); + /// ``` #[inline(always)] pub const fn min_significant_digits(&self) -> OptionUsize { self.min_significant_digits } /// Get the maximum exponent prior to using scientific notation. + /// + /// If the value is set to `300`, then any value with magnitude `>= 1e300` + /// (for base 10) will be writen in exponent notation, while any lower + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `9`. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroI32; + /// + /// use lexical_write_float::Options; + /// + /// const POS_BREAK: Option = NonZeroI32::new(3); + /// const OPTIONS: Options = Options::builder() + /// .positive_exponent_break(POS_BREAK) + /// .build_strict(); + /// assert_eq!(OPTIONS.positive_exponent_break(), POS_BREAK); + /// ``` #[inline(always)] pub const fn positive_exponent_break(&self) -> OptionI32 { self.positive_exponent_break } /// Get the minimum exponent prior to using scientific notation. + /// + /// If the value is set to `-300`, then any value with magnitude `< 1e-300` + /// (for base 10) will be writen in exponent notation, while any larger + /// value will be written in decimal form. If no value is provided, for + /// decimal floats, this defaults to `-5`. + /// + /// # Examples + /// + /// ```rust + /// use core::num::NonZeroI32; + /// + /// use lexical_write_float::Options; + /// + /// const NEG_BREAK: Option = NonZeroI32::new(-3); + /// const OPTIONS: Options = Options::builder() + /// .negative_exponent_break(NEG_BREAK) + /// .build_strict(); + /// assert_eq!(OPTIONS.negative_exponent_break(), NEG_BREAK); + /// ``` #[inline(always)] pub const fn negative_exponent_break(&self) -> OptionI32 { self.negative_exponent_break } + // TODO: HERE + /// Get the rounding mode for writing digits with precision control. + /// + /// For example, writing `1.23456` with 5 significant digits with + /// [`RoundMode::Round`] would produce `"1.2346"` while + /// [`RoundMode::Truncate`] would produce `"1.2345"`. Defaults to + /// [`RoundMode::Round`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::{Options, RoundMode}; + /// + /// const OPTIONS: Options = Options::builder() + /// .round_mode(RoundMode::Truncate) + /// .build_strict(); + /// assert_eq!(OPTIONS.round_mode(), RoundMode::Truncate); + /// ``` #[inline(always)] pub const fn round_mode(&self) -> RoundMode { self.round_mode } - /// Get if we should trim a trailing `".0"` from floats. + /// Get if we should trim a trailing `".0"` from integral floats. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. Defaults to [`false`]. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .trim_floats(true) + /// .build_strict(); + /// assert_eq!(OPTIONS.trim_floats(), true); + /// ``` + /// + /// [`min_significant_digits`]: Self::min_significant_digits #[inline(always)] pub const fn trim_floats(&self) -> bool { self.trim_floats } /// Get the character to designate the exponent component of a float. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `e`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .exponent(b'^') + /// .build_strict(); + /// assert_eq!(OPTIONS.exponent(), b'^'); + /// ``` #[inline(always)] pub const fn exponent(&self) -> u8 { self.exponent } /// Get the character to separate the integer from the fraction components. + /// + /// Any non-control character is valid, but `\t` to `\r` are also valid. + /// The full range is `[0x09, 0x0D]` and `[0x20, 0x7F]`. Defaults to `.`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .exponent(b',') + /// .build_strict(); + /// assert_eq!(OPTIONS.exponent(), b','); + /// ``` #[inline(always)] pub const fn decimal_point(&self) -> u8 { self.decimal_point } /// Get the string representation for `NaN`. + /// + /// The first character must start with `N` or `n` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`NaN`][f64::NAN] returns an error. Defaults to `NaN`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .nan_string(Some(b"nan")) + /// .build_strict(); + /// assert_eq!(OPTIONS.nan_string(), Some(b"nan".as_ref())); + /// ``` #[inline(always)] pub const fn nan_string(&self) -> Option<&'static [u8]> { self.nan_string } - /// Get the short string representation for `Infinity`. + /// Get the string representation for `Infinity`. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .inf_string(Some(b"infinity")) + /// .build_strict(); + /// assert_eq!(OPTIONS.inf_string(), Some(b"infinity".as_ref())); + /// ``` #[inline(always)] pub const fn inf_string(&self) -> Option<&'static [u8]> { self.inf_string } + /// Get the string representation for `Infinity`. Alias for [`inf_string`]. + /// + /// The first character must start with `I` or `i` and all characters must + /// be valid ASCII letters (`A-Z` or `a-z`). If set to `None`, then writing + /// [`Infinity`][f64::INFINITY] returns an error. Defaults to `inf`. + /// + /// [`inf_string`]: Self::inf_string + /// + /// # Examples + /// + /// ```rust + /// use lexical_write_float::Options; + /// + /// const OPTIONS: Options = Options::builder() + /// .infinity_string(Some(b"infinity")) + /// .build_strict(); + /// assert_eq!(OPTIONS.infinity_string(), Some(b"infinity".as_ref())); + /// ``` + #[inline(always)] + pub const fn infinity_string(&self) -> Option<&'static [u8]> { + self.inf_string + } + // SETTERS /// Set the maximum number of significant digits to write. + /// + /// This limits the total number of written digits, truncating based + /// on the [`round_mode`] if more digits would normally be written. + /// + /// # Panics + /// + /// This will panic when writing the float if the value is smaller than + /// [`min_significant_digits`]. + /// + /// [`round_mode`]: Self::round_mode + /// [`min_significant_digits`]: Self::min_significant_digits + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] #[inline(always)] pub fn set_max_significant_digits(&mut self, max_significant_digits: OptionUsize) { self.max_significant_digits = max_significant_digits; } /// Set the minimum number of significant digits to write. + /// + /// If more digits exist, such as writing "1.2" with a minimum of 5 + /// significant digits, then `0`s are appended to the end of the digits. + /// + /// # Panics + /// + /// This will panic when writing the float if the value is larger than + /// [`max_significant_digits`]. + /// + /// [`max_significant_digits`]: Self::max_significant_digits #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_min_significant_digits(&mut self, min_significant_digits: OptionUsize) { self.min_significant_digits = min_significant_digits; } /// Set the maximum exponent prior to using scientific notation. + /// + /// If the value is set to `300`, then any value with magnitude `>= 1e300` + /// (for base 10) will be writen in exponent notation, while any lower + /// value will be written in decimal form. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_positive_exponent_break(&mut self, positive_exponent_break: OptionI32) { self.positive_exponent_break = positive_exponent_break; } /// Set the minimum exponent prior to using scientific notation. + /// + /// If the value is set to `-300`, then any value with magnitude `< 1e-300` + /// (for base 10) will be writen in exponent notation, while any larger + /// value will be written in decimal form. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_negative_exponent_break(&mut self, negative_exponent_break: OptionI32) { self.negative_exponent_break = negative_exponent_break; } /// Set the rounding mode for writing digits with precision control. + /// + /// For example, writing `1.23456` with 5 significant digits with + /// [`RoundMode::Round`] would produce `"1.2346"` while + /// [`RoundMode::Truncate`] would produce `"1.2345"`. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_round_mode(&mut self, round_mode: RoundMode) { self.round_mode = round_mode; } - /// Set if we should trim a trailing `".0"` from floats. + /// Set if we should trim a trailing `".0"` from integral floats. + /// + /// If used in conjunction with [`min_significant_digits`], + /// this will still trim all the significant digits if an integral + /// value is provided. + /// + /// [`min_significant_digits`]: Self::min_significant_digits #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_trim_floats(&mut self, trim_floats: bool) { self.trim_floats = trim_floats; } @@ -555,6 +1701,7 @@ impl Options { /// Always safe, but may produce invalid output if the exponent /// is not a valid ASCII character. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_exponent(&mut self, exponent: u8) { self.exponent = exponent; } @@ -566,6 +1713,7 @@ impl Options { /// Always safe, but may produce invalid output if the decimal point /// is not a valid ASCII character. #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_decimal_point(&mut self, decimal_point: u8) { self.decimal_point = decimal_point; } @@ -579,6 +1727,7 @@ impl Options { /// /// [`FORMATTED_SIZE`]: `lexical_util::constants::FormattedSize::FORMATTED_SIZE` #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_nan_string(&mut self, nan_string: Option<&'static [u8]>) { self.nan_string = nan_string; } @@ -592,19 +1741,20 @@ impl Options { /// /// [`FORMATTED_SIZE`]: `lexical_util::constants::FormattedSize::FORMATTED_SIZE` #[inline(always)] + #[deprecated = "Options should be treated as immutable, use `OptionsBuilder` instead. Will be removed in 2.0."] pub fn set_inf_string(&mut self, inf_string: Option<&'static [u8]>) { self.inf_string = inf_string; } // BUILDERS - /// Get `WriteFloatOptionsBuilder` as a static function. + /// Get [`OptionsBuilder`] as a static function. #[inline(always)] pub const fn builder() -> OptionsBuilder { OptionsBuilder::new() } - /// Create `OptionsBuilder` using existing values. + /// Create [`OptionsBuilder`] using existing values. #[inline(always)] pub const fn rebuild(&self) -> OptionsBuilder { OptionsBuilder { @@ -635,68 +1785,10 @@ impl WriteOptions for Options { Self::is_valid(self) } + #[doc = lexical_util::write_options_doc!()] #[inline(always)] fn buffer_size(&self) -> usize { - let format = NumberFormat::<{ FORMAT }> {}; - - // At least 2 for the decimal point and sign. - let mut count: usize = 2; - - // First need to calculate maximum number of digits from leading or - // trailing zeros, IE, the exponent break. - if !format.no_exponent_notation() { - let min_exp = self.negative_exponent_break().map_or(-5, |x| x.get()); - let max_exp = self.positive_exponent_break().map_or(9, |x| x.get()); - let exp = min_exp.abs().max(max_exp) as usize; - if cfg!(feature = "power-of-two") && exp < 13 { - // 11 for the exponent digits in binary, 1 for the sign, 1 for the symbol - count += 13; - } else if exp < 5 { - // 3 for the exponent digits in decimal, 1 for the sign, 1 for the symbol - count += 5; - } else { - // More leading or trailing zeros than the exponent digits. - count += exp; - } - } else if cfg!(feature = "power-of-two") { - // Min is 2^-1075. - count += 1075; - } else { - // Min is 10^-324. - count += 324; - } - - // Now add the number of significant digits. - let radix = format.radix(); - let formatted_digits = if radix == 10 { - // Really should be 18, but add some extra to be cautious. - 28 - } else { - // BINARY: - // 53 significant mantissa bits for binary, add a few extra. - // RADIX: - // Our limit is `delta`. The maximum relative delta is 2.22e-16, - // around 1. If we have values below 1, our delta is smaller, but - // the max fraction is also a lot smaller. Above, and our fraction - // must be < 1.0, so our delta is less significant. Therefore, - // if our fraction is just less than 1, for a float near 2.0, - // we can do at **maximum** 33 digits (for base 3). Let's just - // assume it's a lot higher, and go with 64. - 64 - }; - let digits = if let Some(max_digits) = self.max_significant_digits() { - formatted_digits.min(max_digits.get()) - } else { - formatted_digits - }; - let digits = if let Some(min_digits) = self.min_significant_digits() { - digits.max(min_digits.get()) - } else { - formatted_digits - }; - count += digits; - - count + self.buffer_size_const::() } } @@ -743,503 +1835,564 @@ const fn unwrap_str(option: Option<&'static [u8]>) -> &'static [u8] { /// Standard number format. #[rustfmt::skip] pub const STANDARD: Options = Options::new(); -const_assert!(STANDARD.is_valid()); /// Numerical format with a decimal comma. +/// /// This is the standard numerical format for most of the world. #[rustfmt::skip] pub const DECIMAL_COMMA: Options = Options::builder() - .decimal_point(b',') - .build_unchecked(); -const_assert!(DECIMAL_COMMA.is_valid()); + .decimal_point(b',') + .build_strict(); /// Numerical format for hexadecimal floats, which use a `p` exponent. #[rustfmt::skip] pub const HEX_FLOAT: Options = Options::builder() - .exponent(b'p') - .build_unchecked(); -const_assert!(HEX_FLOAT.is_valid()); + .exponent(b'p') + .build_strict(); /// Numerical format where `^` is used as the exponent notation character. +/// /// This isn't very common, but is useful when `e` or `p` are valid digits. #[rustfmt::skip] pub const CARAT_EXPONENT: Options = Options::builder() - .exponent(b'^') - .build_unchecked(); -const_assert!(CARAT_EXPONENT.is_valid()); + .exponent(b'^') + .build_strict(); -/// Number format for a `Rust` literal floating-point number. +/// Number format for a [`Rust`] literal floating-point number. +/// +/// [`Rust`]: https://www.rust-lang.org/ #[rustfmt::skip] pub const RUST_LITERAL: Options = Options::builder() - .nan_string(options::RUST_LITERAL) - .inf_string(options::RUST_LITERAL) - .build_unchecked(); -const_assert!(RUST_LITERAL.is_valid()); + .nan_string(options::RUST_LITERAL) + .inf_string(options::RUST_LITERAL) + .build_strict(); -/// Number format for a `Python` literal floating-point number. +/// Number format for a [`Python`] literal floating-point number. +/// +/// [`Python`]: https://www.python.org/ #[rustfmt::skip] pub const PYTHON_LITERAL: Options = Options::builder() - .nan_string(options::PYTHON_LITERAL) - .inf_string(options::PYTHON_LITERAL) - .build_unchecked(); -const_assert!(PYTHON_LITERAL.is_valid()); + .nan_string(options::PYTHON_LITERAL) + .inf_string(options::PYTHON_LITERAL) + .build_strict(); -/// Number format for a `C++` literal floating-point number. +/// Number format for a [`C++`] literal floating-point number. +/// +/// [`C++`]: https://en.cppreference.com/w/ #[rustfmt::skip] pub const CXX_LITERAL: Options = Options::builder() - .nan_string(options::CXX_LITERAL_NAN) - .inf_string(options::CXX_LITERAL_INF) - .build_unchecked(); -const_assert!(CXX_LITERAL.is_valid()); + .nan_string(options::CXX_LITERAL_NAN) + .inf_string(options::CXX_LITERAL_INF) + .build_strict(); -/// Number format for a `C` literal floating-point number. +/// Number format for a [`C`] literal floating-point number. +/// +/// [`C`]: https://en.cppreference.com/w/c #[rustfmt::skip] pub const C_LITERAL: Options = Options::builder() - .nan_string(options::C_LITERAL_NAN) - .inf_string(options::C_LITERAL_INF) - .build_unchecked(); -const_assert!(CXX_LITERAL.is_valid()); + .nan_string(options::C_LITERAL_NAN) + .inf_string(options::C_LITERAL_INF) + .build_strict(); -/// Number format for a `Ruby` literal floating-point number. +/// Number format for a [`Ruby`] literal floating-point number. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ #[rustfmt::skip] pub const RUBY_LITERAL: Options = Options::builder() - .positive_exponent_break(num::NonZeroI32::new(14)) - .negative_exponent_break(num::NonZeroI32::new(-4)) - .nan_string(options::RUBY_LITERAL_NAN) - .inf_string(options::RUBY_LITERAL_INF) - .build_unchecked(); -const_assert!(RUBY_LITERAL.is_valid()); + .positive_exponent_break(num::NonZeroI32::new(14)) + .negative_exponent_break(num::NonZeroI32::new(-4)) + .nan_string(options::RUBY_LITERAL_NAN) + .inf_string(options::RUBY_LITERAL_INF) + .build_strict(); -/// Number format to parse a `Ruby` float from string. +/// Number format to parse a [`Ruby`] float from string. +/// +/// [`Ruby`]: https://www.ruby-lang.org/en/ #[rustfmt::skip] pub const RUBY_STRING: Options = Options::builder() - .nan_string(options::RUBY_LITERAL_NAN) - .inf_string(options::RUBY_LITERAL_INF) - .build_unchecked(); -const_assert!(RUBY_STRING.is_valid()); + .nan_string(options::RUBY_LITERAL_NAN) + .inf_string(options::RUBY_LITERAL_INF) + .build_strict(); -/// Number format for a `Swift` literal floating-point number. +/// Number format for a [`Swift`] literal floating-point number. +/// +/// [`Swift`]: https://developer.apple.com/swift/ #[rustfmt::skip] pub const SWIFT_LITERAL: Options = Options::builder() - .nan_string(options::SWIFT_LITERAL) - .inf_string(options::SWIFT_LITERAL) - .build_unchecked(); -const_assert!(SWIFT_LITERAL.is_valid()); + .nan_string(options::SWIFT_LITERAL) + .inf_string(options::SWIFT_LITERAL) + .build_strict(); -/// Number format for a `Go` literal floating-point number. +/// Number format for a [`Golang`] literal floating-point number. +/// +/// [`Golang`]: https://go.dev/ #[rustfmt::skip] pub const GO_LITERAL: Options = Options::builder() - .nan_string(options::GO_LITERAL) - .inf_string(options::GO_LITERAL) - .build_unchecked(); -const_assert!(GO_LITERAL.is_valid()); + .nan_string(options::GO_LITERAL) + .inf_string(options::GO_LITERAL) + .build_strict(); -/// Number format for a `Haskell` literal floating-point number. +/// Number format for a [`Haskell`] literal floating-point number. +/// +/// [`Haskell`]: https://www.haskell.org/ #[rustfmt::skip] pub const HASKELL_LITERAL: Options = Options::builder() - .nan_string(options::HASKELL_LITERAL) - .inf_string(options::HASKELL_LITERAL) - .build_unchecked(); -const_assert!(HASKELL_LITERAL.is_valid()); + .nan_string(options::HASKELL_LITERAL) + .inf_string(options::HASKELL_LITERAL) + .build_strict(); -/// Number format to parse a `Haskell` float from string. +/// Number format to parse a [`Haskell`] float from string. +/// +/// [`Haskell`]: https://www.haskell.org/ #[rustfmt::skip] pub const HASKELL_STRING: Options = Options::builder() - .inf_string(options::HASKELL_STRING_INF) - .build_unchecked(); -const_assert!(HASKELL_STRING.is_valid()); + .inf_string(options::HASKELL_STRING_INF) + .build_strict(); -/// Number format for a `Javascript` literal floating-point number. +/// Number format for a [`Javascript`] literal floating-point number. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript #[rustfmt::skip] pub const JAVASCRIPT_LITERAL: Options = Options::builder() - .inf_string(options::JAVASCRIPT_INF) - .build_unchecked(); -const_assert!(JAVASCRIPT_LITERAL.is_valid()); + .inf_string(options::JAVASCRIPT_INF) + .build_strict(); -/// Number format to parse a `Javascript` float from string. +/// Number format to parse a [`Javascript`] float from string. +/// +/// [`Javascript`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript #[rustfmt::skip] pub const JAVASCRIPT_STRING: Options = Options::builder() - .inf_string(options::JAVASCRIPT_INF) - .build_unchecked(); -const_assert!(JAVASCRIPT_STRING.is_valid()); + .inf_string(options::JAVASCRIPT_INF) + .build_strict(); -/// Number format for a `Perl` literal floating-point number. +/// Number format for a [`Perl`] literal floating-point number. +/// +/// [`Perl`]: https://www.perl.org/ #[rustfmt::skip] pub const PERL_LITERAL: Options = Options::builder() - .nan_string(options::PERL_LITERAL) - .inf_string(options::PERL_LITERAL) - .build_unchecked(); -const_assert!(PERL_LITERAL.is_valid()); + .nan_string(options::PERL_LITERAL) + .inf_string(options::PERL_LITERAL) + .build_strict(); -/// Number format for a `PHP` literal floating-point number. +/// Number format for a [`PHP`] literal floating-point number. +/// +/// [`PHP`]: https://www.php.net/ #[rustfmt::skip] pub const PHP_LITERAL: Options = Options::builder() - .nan_string(options::PHP_LITERAL_NAN) - .inf_string(options::PHP_LITERAL_INF) - .build_unchecked(); -const_assert!(PHP_LITERAL.is_valid()); + .nan_string(options::PHP_LITERAL_NAN) + .inf_string(options::PHP_LITERAL_INF) + .build_strict(); -/// Number format for a `Java` literal floating-point number. +/// Number format for a [`Java`] literal floating-point number. +/// +/// [`Java`]: https://www.java.com/en/ #[rustfmt::skip] pub const JAVA_LITERAL: Options = Options::builder() - .nan_string(options::JAVA_LITERAL) - .inf_string(options::JAVA_LITERAL) - .build_unchecked(); -const_assert!(JAVA_LITERAL.is_valid()); + .nan_string(options::JAVA_LITERAL) + .inf_string(options::JAVA_LITERAL) + .build_strict(); -/// Number format to parse a `Java` float from string. +/// Number format to parse a [`Java`] float from string. +/// +/// [`Java`]: https://www.java.com/en/ #[rustfmt::skip] pub const JAVA_STRING: Options = Options::builder() - .inf_string(options::JAVA_STRING_INF) - .build_unchecked(); -const_assert!(JAVA_STRING.is_valid()); + .inf_string(options::JAVA_STRING_INF) + .build_strict(); -/// Number format for an `R` literal floating-point number. +/// Number format for an [`R`] literal floating-point number. +/// +/// [`R`]: https://www.r-project.org/ #[rustfmt::skip] pub const R_LITERAL: Options = Options::builder() - .inf_string(options::R_LITERAL_INF) - .build_unchecked(); -const_assert!(R_LITERAL.is_valid()); + .inf_string(options::R_LITERAL_INF) + .build_strict(); -/// Number format for a `Kotlin` literal floating-point number. +/// Number format for a [`Kotlin`] literal floating-point number. +/// +/// [`Kotlin`]: https://kotlinlang.org/ #[rustfmt::skip] pub const KOTLIN_LITERAL: Options = Options::builder() - .nan_string(options::KOTLIN_LITERAL) - .inf_string(options::KOTLIN_LITERAL) - .build_unchecked(); -const_assert!(KOTLIN_LITERAL.is_valid()); + .nan_string(options::KOTLIN_LITERAL) + .inf_string(options::KOTLIN_LITERAL) + .build_strict(); -/// Number format to parse a `Kotlin` float from string. +/// Number format to parse a [`Kotlin`] float from string. +/// +/// [`Kotlin`]: https://kotlinlang.org/ #[rustfmt::skip] pub const KOTLIN_STRING: Options = Options::builder() - .inf_string(options::KOTLIN_STRING_INF) - .build_unchecked(); -const_assert!(KOTLIN_STRING.is_valid()); + .inf_string(options::KOTLIN_STRING_INF) + .build_strict(); -/// Number format for a `Julia` literal floating-point number. +/// Number format for a [`Julia`] literal floating-point number. +/// +/// [`Julia`]: https://julialang.org/ #[rustfmt::skip] pub const JULIA_LITERAL: Options = Options::builder() - .inf_string(options::JULIA_LITERAL_INF) - .build_unchecked(); -const_assert!(JULIA_LITERAL.is_valid()); + .inf_string(options::JULIA_LITERAL_INF) + .build_strict(); -/// Number format for a `C#` literal floating-point number. +/// Number format for a [`C#`] literal floating-point number. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ #[rustfmt::skip] pub const CSHARP_LITERAL: Options = Options::builder() - .nan_string(options::CSHARP_LITERAL) - .inf_string(options::CSHARP_LITERAL) - .build_unchecked(); -const_assert!(CSHARP_LITERAL.is_valid()); + .nan_string(options::CSHARP_LITERAL) + .inf_string(options::CSHARP_LITERAL) + .build_strict(); -/// Number format to parse a `C#` float from string. +/// Number format to parse a [`C#`] float from string. +/// +/// [`C#`]: https://learn.microsoft.com/en-us/dotnet/csharp/ #[rustfmt::skip] pub const CSHARP_STRING: Options = Options::builder() - .inf_string(options::CSHARP_STRING_INF) - .build_unchecked(); -const_assert!(CSHARP_STRING.is_valid()); + .inf_string(options::CSHARP_STRING_INF) + .build_strict(); -/// Number format for a `Kawa` literal floating-point number. +/// Number format for a [`Kawa`] literal floating-point number. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ #[rustfmt::skip] pub const KAWA_LITERAL: Options = Options::builder() - .nan_string(options::KAWA) - .inf_string(options::KAWA) - .build_unchecked(); -const_assert!(KAWA_LITERAL.is_valid()); + .nan_string(options::KAWA) + .inf_string(options::KAWA) + .build_strict(); -/// Number format to parse a `Kawa` float from string. +/// Number format to parse a [`Kawa`] float from string. +/// +/// [`Kawa`]: https://www.gnu.org/software/kawa/ #[rustfmt::skip] pub const KAWA_STRING: Options = Options::builder() - .nan_string(options::KAWA) - .inf_string(options::KAWA) - .build_unchecked(); -const_assert!(KAWA_STRING.is_valid()); + .nan_string(options::KAWA) + .inf_string(options::KAWA) + .build_strict(); -/// Number format for a `Gambit-C` literal floating-point number. +/// Number format for a [`Gambit-C`] literal floating-point number. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ #[rustfmt::skip] pub const GAMBITC_LITERAL: Options = Options::builder() - .nan_string(options::GAMBITC) - .inf_string(options::GAMBITC) - .build_unchecked(); -const_assert!(GAMBITC_LITERAL.is_valid()); + .nan_string(options::GAMBITC) + .inf_string(options::GAMBITC) + .build_strict(); -/// Number format to parse a `Gambit-C` float from string. +/// Number format to parse a [`Gambit-C`] float from string. +/// +/// [`Gambit-C`]: https://gambitscheme.org/ #[rustfmt::skip] pub const GAMBITC_STRING: Options = Options::builder() - .nan_string(options::GAMBITC) - .inf_string(options::GAMBITC) - .build_unchecked(); -const_assert!(GAMBITC_STRING.is_valid()); + .nan_string(options::GAMBITC) + .inf_string(options::GAMBITC) + .build_strict(); -/// Number format for a `Guile` literal floating-point number. +/// Number format for a [`Guile`] literal floating-point number. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ #[rustfmt::skip] pub const GUILE_LITERAL: Options = Options::builder() - .nan_string(options::GUILE) - .inf_string(options::GUILE) - .build_unchecked(); -const_assert!(GUILE_LITERAL.is_valid()); + .nan_string(options::GUILE) + .inf_string(options::GUILE) + .build_strict(); -/// Number format to parse a `Guile` float from string. +/// Number format to parse a [`Guile`] float from string. +/// +/// [`Guile`]: https://www.gnu.org/software/guile/ #[rustfmt::skip] pub const GUILE_STRING: Options = Options::builder() - .nan_string(options::GUILE) - .inf_string(options::GUILE) - .build_unchecked(); -const_assert!(GUILE_STRING.is_valid()); + .nan_string(options::GUILE) + .inf_string(options::GUILE) + .build_strict(); -/// Number format for a `Clojure` literal floating-point number. +/// Number format for a [`Clojure`] literal floating-point number. +/// +/// [`Clojure`]: https://clojure.org/ #[rustfmt::skip] pub const CLOJURE_LITERAL: Options = Options::builder() - .nan_string(options::CLOJURE_LITERAL) - .inf_string(options::CLOJURE_LITERAL) - .build_unchecked(); -const_assert!(CLOJURE_LITERAL.is_valid()); + .nan_string(options::CLOJURE_LITERAL) + .inf_string(options::CLOJURE_LITERAL) + .build_strict(); -/// Number format to parse a `Clojure` float from string. +/// Number format to parse a [`Clojure`] float from string. +/// +/// [`Clojure`]: https://clojure.org/ #[rustfmt::skip] pub const CLOJURE_STRING: Options = Options::builder() - .inf_string(options::CLOJURE_STRING_INF) - .build_unchecked(); -const_assert!(CLOJURE_STRING.is_valid()); + .inf_string(options::CLOJURE_STRING_INF) + .build_strict(); -/// Number format for an `Erlang` literal floating-point number. +/// Number format for an [`Erlang`] literal floating-point number. +/// +/// [`Erlang`]: https://www.erlang.org/ #[rustfmt::skip] pub const ERLANG_LITERAL: Options = Options::builder() - .nan_string(options::ERLANG_LITERAL_NAN) - .build_unchecked(); -const_assert!(ERLANG_LITERAL.is_valid()); + .nan_string(options::ERLANG_LITERAL_NAN) + .build_strict(); -/// Number format to parse an `Erlang` float from string. +/// Number format to parse an [`Erlang`] float from string. +/// +/// [`Erlang`]: https://www.erlang.org/ #[rustfmt::skip] pub const ERLANG_STRING: Options = Options::builder() - .nan_string(options::ERLANG_STRING) - .inf_string(options::ERLANG_STRING) - .build_unchecked(); -const_assert!(ERLANG_STRING.is_valid()); + .nan_string(options::ERLANG_STRING) + .inf_string(options::ERLANG_STRING) + .build_strict(); -/// Number format for an `Elm` literal floating-point number. +/// Number format for an [`Elm`] literal floating-point number. +/// +/// [`Elm`]: https://elm-lang.org/ #[rustfmt::skip] pub const ELM_LITERAL: Options = Options::builder() - .nan_string(options::ELM_LITERAL) - .inf_string(options::ELM_LITERAL) - .build_unchecked(); -const_assert!(ELM_LITERAL.is_valid()); + .nan_string(options::ELM_LITERAL) + .inf_string(options::ELM_LITERAL) + .build_strict(); -/// Number format to parse an `Elm` float from string. +/// Number format to parse an [`Elm`] float from string. +/// +/// [`Elm`]: https://elm-lang.org/ #[rustfmt::skip] pub const ELM_STRING: Options = Options::builder() - .nan_string(options::ELM_STRING_NAN) - .inf_string(options::ELM_STRING_INF) - .build_unchecked(); -const_assert!(ELM_STRING.is_valid()); + .nan_string(options::ELM_STRING_NAN) + .inf_string(options::ELM_STRING_INF) + .build_strict(); -/// Number format for a `Scala` literal floating-point number. +/// Number format for a [`Scala`] literal floating-point number. +/// +/// [`Scala`]: https://www.scala-lang.org/ #[rustfmt::skip] pub const SCALA_LITERAL: Options = Options::builder() - .nan_string(options::SCALA_LITERAL) - .inf_string(options::SCALA_LITERAL) - .build_unchecked(); -const_assert!(SCALA_LITERAL.is_valid()); + .nan_string(options::SCALA_LITERAL) + .inf_string(options::SCALA_LITERAL) + .build_strict(); -/// Number format to parse a `Scala` float from string. +/// Number format to parse a [`Scala`] float from string. +/// +/// [`Scala`]: https://www.scala-lang.org/ #[rustfmt::skip] pub const SCALA_STRING: Options = Options::builder() - .inf_string(options::SCALA_STRING_INF) - .build_unchecked(); -const_assert!(SCALA_STRING.is_valid()); + .inf_string(options::SCALA_STRING_INF) + .build_strict(); -/// Number format for an `Elixir` literal floating-point number. +/// Number format for an [`Elixir`] literal floating-point number. +/// +/// [`Elixir`]: https://elixir-lang.org/ #[rustfmt::skip] pub const ELIXIR_LITERAL: Options = Options::builder() - .nan_string(options::ELIXIR) - .inf_string(options::ELIXIR) - .build_unchecked(); -const_assert!(ELIXIR_LITERAL.is_valid()); + .nan_string(options::ELIXIR) + .inf_string(options::ELIXIR) + .build_strict(); -/// Number format to parse an `Elixir` float from string. +/// Number format to parse an [`Elixir`] float from string. +/// +/// [`Elixir`]: https://elixir-lang.org/ #[rustfmt::skip] pub const ELIXIR_STRING: Options = Options::builder() - .nan_string(options::ELIXIR) - .inf_string(options::ELIXIR) - .build_unchecked(); -const_assert!(ELIXIR_STRING.is_valid()); + .nan_string(options::ELIXIR) + .inf_string(options::ELIXIR) + .build_strict(); -/// Number format for a `FORTRAN` literal floating-point number. +/// Number format for a [`FORTRAN`] literal floating-point number. +/// +/// [`FORTRAN`]: https://fortran-lang.org/ #[rustfmt::skip] pub const FORTRAN_LITERAL: Options = Options::builder() - .nan_string(options::FORTRAN_LITERAL) - .inf_string(options::FORTRAN_LITERAL) - .build_unchecked(); -const_assert!(FORTRAN_LITERAL.is_valid()); + .nan_string(options::FORTRAN_LITERAL) + .inf_string(options::FORTRAN_LITERAL) + .build_strict(); -/// Number format for a `D` literal floating-point number. +/// Number format for a [`D`] literal floating-point number. +/// +/// [`D`]: https://dlang.org/ #[rustfmt::skip] pub const D_LITERAL: Options = Options::builder() - .nan_string(options::D_LITERAL) - .inf_string(options::D_LITERAL) - .build_unchecked(); -const_assert!(D_LITERAL.is_valid()); + .nan_string(options::D_LITERAL) + .inf_string(options::D_LITERAL) + .build_strict(); -/// Number format for a `Coffeescript` literal floating-point number. +/// Number format for a [`Coffeescript`] literal floating-point number. +/// +/// [`Coffeescript`]: https://coffeescript.org/ #[rustfmt::skip] pub const COFFEESCRIPT_LITERAL: Options = Options::builder() - .inf_string(options::COFFEESCRIPT_INF) - .build_unchecked(); -const_assert!(COFFEESCRIPT_LITERAL.is_valid()); + .inf_string(options::COFFEESCRIPT_INF) + .build_strict(); -/// Number format to parse a `Coffeescript` float from string. +/// Number format to parse a [`Coffeescript`] float from string. +/// +/// [`Coffeescript`]: https://coffeescript.org/ #[rustfmt::skip] pub const COFFEESCRIPT_STRING: Options = Options::builder() - .inf_string(options::COFFEESCRIPT_INF) - .build_unchecked(); -const_assert!(COFFEESCRIPT_STRING.is_valid()); + .inf_string(options::COFFEESCRIPT_INF) + .build_strict(); -/// Number format for a `COBOL` literal floating-point number. +/// Number format for a [`COBOL`] literal floating-point number. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol #[rustfmt::skip] pub const COBOL_LITERAL: Options = Options::builder() - .nan_string(options::COBOL) - .inf_string(options::COBOL) - .build_unchecked(); -const_assert!(COBOL_LITERAL.is_valid()); + .nan_string(options::COBOL) + .inf_string(options::COBOL) + .build_strict(); -/// Number format to parse a `COBOL` float from string. +/// Number format to parse a [`COBOL`] float from string. +/// +/// [`Cobol`]: https://www.ibm.com/think/topics/cobol #[rustfmt::skip] pub const COBOL_STRING: Options = Options::builder() - .nan_string(options::COBOL) - .inf_string(options::COBOL) - .build_unchecked(); -const_assert!(COBOL_STRING.is_valid()); + .nan_string(options::COBOL) + .inf_string(options::COBOL) + .build_strict(); -/// Number format for an `F#` literal floating-point number. +/// Number format for an [`F#`] literal floating-point number. +/// +/// [`F#`]: https://fsharp.org/ #[rustfmt::skip] pub const FSHARP_LITERAL: Options = Options::builder() - .nan_string(options::FSHARP_LITERAL_NAN) - .inf_string(options::FSHARP_LITERAL_INF) - .build_unchecked(); -const_assert!(FSHARP_LITERAL.is_valid()); + .nan_string(options::FSHARP_LITERAL_NAN) + .inf_string(options::FSHARP_LITERAL_INF) + .build_strict(); -/// Number format for a `Visual Basic` literal floating-point number. +/// Number format for a [`Visual Basic`] literal floating-point number. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ #[rustfmt::skip] pub const VB_LITERAL: Options = Options::builder() - .nan_string(options::VB_LITERAL) - .inf_string(options::VB_LITERAL) - .build_unchecked(); -const_assert!(VB_LITERAL.is_valid()); + .nan_string(options::VB_LITERAL) + .inf_string(options::VB_LITERAL) + .build_strict(); -/// Number format to parse a `Visual Basic` float from string. +/// Number format to parse a [`Visual Basic`] float from string. +/// +/// [`Visual Basic`]: https://learn.microsoft.com/en-us/dotnet/visual-basic/ #[rustfmt::skip] pub const VB_STRING: Options = Options::builder() - .inf_string(options::VB_STRING_INF) - .build_unchecked(); -const_assert!(VB_STRING.is_valid()); + .inf_string(options::VB_STRING_INF) + .build_strict(); -/// Number format for an `OCaml` literal floating-point number. +/// Number format for an [`OCaml`] literal floating-point number. +/// +/// [`OCaml`]: https://ocaml.org/ #[rustfmt::skip] pub const OCAML_LITERAL: Options = Options::builder() - .nan_string(options::OCAML_LITERAL_NAN) - .inf_string(options::OCAML_LITERAL_INF) - .build_unchecked(); -const_assert!(OCAML_LITERAL.is_valid()); + .nan_string(options::OCAML_LITERAL_NAN) + .inf_string(options::OCAML_LITERAL_INF) + .build_strict(); -/// Number format for an `Objective-C` literal floating-point number. +/// Number format for an [`Objective-C`] literal floating-point number. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C #[rustfmt::skip] pub const OBJECTIVEC_LITERAL: Options = Options::builder() - .nan_string(options::OBJECTIVEC) - .inf_string(options::OBJECTIVEC) - .build_unchecked(); -const_assert!(OBJECTIVEC_LITERAL.is_valid()); + .nan_string(options::OBJECTIVEC) + .inf_string(options::OBJECTIVEC) + .build_strict(); -/// Number format to parse an `Objective-C` float from string. +/// Number format to parse an [`Objective-C`] float from string. +/// +/// [`Objective-C`]: https://en.wikipedia.org/wiki/Objective-C #[rustfmt::skip] pub const OBJECTIVEC_STRING: Options = Options::builder() - .nan_string(options::OBJECTIVEC) - .inf_string(options::OBJECTIVEC) - .build_unchecked(); -const_assert!(OBJECTIVEC_STRING.is_valid()); + .nan_string(options::OBJECTIVEC) + .inf_string(options::OBJECTIVEC) + .build_strict(); -/// Number format for an `ReasonML` literal floating-point number. +/// Number format for an [`ReasonML`] literal floating-point number. +/// +/// [`ReasonML`]: https://reasonml.github.io/ #[rustfmt::skip] pub const REASONML_LITERAL: Options = Options::builder() - .nan_string(options::REASONML_LITERAL_NAN) - .inf_string(options::REASONML_LITERAL_INF) - .build_unchecked(); -const_assert!(REASONML_LITERAL.is_valid()); + .nan_string(options::REASONML_LITERAL_NAN) + .inf_string(options::REASONML_LITERAL_INF) + .build_strict(); -/// Number format for a `MATLAB` literal floating-point number. +/// Number format for a [`MATLAB`] literal floating-point number. +/// +/// [`Matlab`]: https://www.mathworks.com/products/matlab.html #[rustfmt::skip] pub const MATLAB_LITERAL: Options = Options::builder() - .inf_string(options::MATLAB_LITERAL_INF) - .build_unchecked(); -const_assert!(MATLAB_LITERAL.is_valid()); + .inf_string(options::MATLAB_LITERAL_INF) + .build_strict(); -/// Number format for a `Zig` literal floating-point number. +/// Number format for a [`Zig`] literal floating-point number. +/// +/// [`Zig`]: https://ziglang.org/ #[rustfmt::skip] pub const ZIG_LITERAL: Options = Options::builder() - .nan_string(options::ZIG_LITERAL) - .inf_string(options::ZIG_LITERAL) - .build_unchecked(); -const_assert!(ZIG_LITERAL.is_valid()); + .nan_string(options::ZIG_LITERAL) + .inf_string(options::ZIG_LITERAL) + .build_strict(); -/// Number format for a `Safe` literal floating-point number. +/// Number format for a [`Sage`] literal floating-point number. +/// +/// [`Sage`]: https://www.sagemath.org/ #[rustfmt::skip] pub const SAGE_LITERAL: Options = Options::builder() - .inf_string(options::SAGE_LITERAL_INF) - .build_unchecked(); -const_assert!(SAGE_LITERAL.is_valid()); + .inf_string(options::SAGE_LITERAL_INF) + .build_strict(); -/// Number format for a `JSON` literal floating-point number. +/// Number format for a [`JSON`][`JSON-REF`] literal floating-point number. +/// +/// [`JSON-REF`]: https://www.json.org/json-en.html #[rustfmt::skip] pub const JSON: Options = Options::builder() - .nan_string(options::JSON) - .inf_string(options::JSON) - .build_unchecked(); -const_assert!(JSON.is_valid()); + .nan_string(options::JSON) + .inf_string(options::JSON) + .build_strict(); -/// Number format for a `TOML` literal floating-point number. +/// Number format for a [`TOML`][`TOML-REF`] literal floating-point number. +/// +/// [`TOML-REF`]: https://toml.io/en/ #[rustfmt::skip] pub const TOML: Options = Options::builder() - .nan_string(options::TOML) - .inf_string(options::TOML) - .build_unchecked(); -const_assert!(TOML.is_valid()); + .nan_string(options::TOML) + .inf_string(options::TOML) + .build_strict(); -/// Number format for a `YAML` literal floating-point number. +/// Number format for a [`YAML`][`YAML-REF`] literal floating-point number. +/// +/// [`YAML-REF`]: https://yaml.org/ #[rustfmt::skip] pub const YAML: Options = JSON; -/// Number format for an `XML` literal floating-point number. +/// Number format for an [`XML`][`XML-REF`] literal floating-point number. +/// +/// [`XML-REF`]: https://en.wikipedia.org/wiki/XML #[rustfmt::skip] pub const XML: Options = Options::builder() - .inf_string(options::XML_INF) - .build_unchecked(); -const_assert!(XML.is_valid()); + .inf_string(options::XML_INF) + .build_strict(); -/// Number format for a `SQLite` literal floating-point number. +/// Number format for a [`SQLite`] literal floating-point number. +/// +/// [`SQLite`]: https://www.sqlite.org/ #[rustfmt::skip] pub const SQLITE: Options = Options::builder() - .nan_string(options::SQLITE) - .inf_string(options::SQLITE) - .build_unchecked(); -const_assert!(SQLITE.is_valid()); + .nan_string(options::SQLITE) + .inf_string(options::SQLITE) + .build_strict(); -/// Number format for a `PostgreSQL` literal floating-point number. +/// Number format for a [`PostgreSQL`] literal floating-point number. +/// +/// [`PostgreSQL`]: https://www.postgresql.org/ #[rustfmt::skip] pub const POSTGRESQL: Options = Options::builder() - .nan_string(options::POSTGRESQL) - .inf_string(options::POSTGRESQL) - .build_unchecked(); -const_assert!(POSTGRESQL.is_valid()); + .nan_string(options::POSTGRESQL) + .inf_string(options::POSTGRESQL) + .build_strict(); -/// Number format for a `MySQL` literal floating-point number. +/// Number format for a [`MySQL`] literal floating-point number. +/// +/// [`MySQL`]: https://www.mysql.com/ #[rustfmt::skip] pub const MYSQL: Options = Options::builder() - .nan_string(options::MYSQL) - .inf_string(options::MYSQL) - .build_unchecked(); -const_assert!(MYSQL.is_valid()); + .nan_string(options::MYSQL) + .inf_string(options::MYSQL) + .build_strict(); -/// Number format for a `MongoDB` literal floating-point number. +/// Number format for a [`MongoDB`] literal floating-point number. +/// +/// [`MongoDB`]: https://www.mongodb.com/ #[rustfmt::skip] pub const MONGODB: Options = Options::builder() - .inf_string(options::MONGODB_INF) - .build_unchecked(); -const_assert!(MONGODB.is_valid()); + .inf_string(options::MONGODB_INF) + .build_strict(); diff --git a/lexical-write-float/src/write.rs b/lexical-write-float/src/write.rs index 8b3c54fa..b33ebaaa 100644 --- a/lexical-write-float/src/write.rs +++ b/lexical-write-float/src/write.rs @@ -7,7 +7,6 @@ use lexical_util::bf16::bf16; #[cfg(feature = "f16")] use lexical_util::f16::f16; use lexical_util::format::NumberFormat; -use lexical_util::options::WriteOptions; use lexical_util::{algorithm::copy_to_dst, constants::FormattedSize}; use lexical_write_integer::write::WriteInteger; @@ -64,7 +63,7 @@ fn check_buffer(len: usize, options: &Options) -> bool where T: FormattedSize, { - let size = Options::buffer_size::(options); + let size = Options::buffer_size_const::(options); len >= size } diff --git a/lexical-write-float/tests/algorithm_tests.rs b/lexical-write-float/tests/algorithm_tests.rs index 5a6b35db..14844f06 100644 --- a/lexical-write-float/tests/algorithm_tests.rs +++ b/lexical-write-float/tests/algorithm_tests.rs @@ -264,77 +264,77 @@ fn write_float_scientific(mant: u64, exp: i32, options: &Options, expected: &str #[test] fn write_float_scientific_test() { - let options = Options::new(); - write_float_scientific(1, 0, &options, "1.0e0"); - write_float_scientific(1, 3, &options, "1.0e3"); - write_float_scientific(1, -12, &options, "1.0e-12"); - write_float_scientific(999999999999999, -15, &options, "9.99999999999999e-1"); - write_float_scientific(999999999999999, -14, &options, "9.99999999999999e0"); - write_float_scientific(999999999999999, -16, &options, "9.99999999999999e-2"); - write_float_scientific(17976931348623157, 292, &options, "1.7976931348623157e308"); - write_float_scientific(22250738585072014, -324, &options, "2.2250738585072014e-308"); - - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build().unwrap(); - write_float_scientific(1, 0, &options, "1.0000000000000000000000000000000000000000000000000e0"); - write_float_scientific(1, 3, &options, "1.0000000000000000000000000000000000000000000000000e3"); + const OPTS1: Options = Options::new(); + write_float_scientific(1, 0, &OPTS1, "1.0e0"); + write_float_scientific(1, 3, &OPTS1, "1.0e3"); + write_float_scientific(1, -12, &OPTS1, "1.0e-12"); + write_float_scientific(999999999999999, -15, &OPTS1, "9.99999999999999e-1"); + write_float_scientific(999999999999999, -14, &OPTS1, "9.99999999999999e0"); + write_float_scientific(999999999999999, -16, &OPTS1, "9.99999999999999e-2"); + write_float_scientific(17976931348623157, 292, &OPTS1, "1.7976931348623157e308"); + write_float_scientific(22250738585072014, -324, &OPTS1, "2.2250738585072014e-308"); + + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build_strict(); + write_float_scientific(1, 0, &OPTS2, "1.0000000000000000000000000000000000000000000000000e0"); + write_float_scientific(1, 3, &OPTS2, "1.0000000000000000000000000000000000000000000000000e3"); write_float_scientific( 1, -12, - &options, + &OPTS2, "1.0000000000000000000000000000000000000000000000000e-12", ); write_float_scientific( 999999999999999, -15, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000e-1", ); write_float_scientific( 999999999999999, -14, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000e0", ); write_float_scientific( 999999999999999, -16, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000e-2", ); write_float_scientific( 17976931348623157, 292, - &options, + &OPTS2, "1.7976931348623157000000000000000000000000000000000e308", ); write_float_scientific( 22250738585072014, -324, - &options, + &OPTS2, "2.2250738585072014000000000000000000000000000000000e-308", ); - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_scientific(1, 0, &options, "1.0e0"); - write_float_scientific(1, 3, &options, "1.0e3"); - write_float_scientific(1, -12, &options, "1.0e-12"); - write_float_scientific(999999999999999, -15, &options, "1.0e0"); - write_float_scientific(999999999999999, -14, &options, "1.0e1"); - write_float_scientific(999999999999999, -16, &options, "1.0e-1"); - write_float_scientific(17976931348623157, 292, &options, "1.7977e308"); - write_float_scientific(22250738585072014, -324, &options, "2.2251e-308"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_scientific(1, 0, &options, "1e0"); - write_float_scientific(1, 3, &options, "1e3"); - write_float_scientific(1, -12, &options, "1e-12"); - write_float_scientific(999999999999999, -15, &options, "9.99999999999999e-1"); - write_float_scientific(999999999999999, -14, &options, "9.99999999999999e0"); - write_float_scientific(999999999999999, -16, &options, "9.99999999999999e-2"); - write_float_scientific(17976931348623157, 292, &options, "1.7976931348623157e308"); - write_float_scientific(22250738585072014, -324, &options, "2.2250738585072014e-308"); + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_scientific(1, 0, &OPTS3, "1.0e0"); + write_float_scientific(1, 3, &OPTS3, "1.0e3"); + write_float_scientific(1, -12, &OPTS3, "1.0e-12"); + write_float_scientific(999999999999999, -15, &OPTS3, "1.0e0"); + write_float_scientific(999999999999999, -14, &OPTS3, "1.0e1"); + write_float_scientific(999999999999999, -16, &OPTS3, "1.0e-1"); + write_float_scientific(17976931348623157, 292, &OPTS3, "1.7977e308"); + write_float_scientific(22250738585072014, -324, &OPTS3, "2.2251e-308"); + + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_scientific(1, 0, &OPTS4, "1e0"); + write_float_scientific(1, 3, &OPTS4, "1e3"); + write_float_scientific(1, -12, &OPTS4, "1e-12"); + write_float_scientific(999999999999999, -15, &OPTS4, "9.99999999999999e-1"); + write_float_scientific(999999999999999, -14, &OPTS4, "9.99999999999999e0"); + write_float_scientific(999999999999999, -16, &OPTS4, "9.99999999999999e-2"); + write_float_scientific(17976931348623157, 292, &OPTS4, "1.7976931348623157e308"); + write_float_scientific(22250738585072014, -324, &OPTS4, "2.2250738585072014e-308"); } fn write_float_positive_exponent(mant: u64, exp: i32, options: &Options, expected: &str) { @@ -357,73 +357,73 @@ fn write_float_positive_exponent(mant: u64, exp: i32, options: &Options, expecte #[test] fn write_float_positive_exponent_test() { - let options = Options::new(); - write_float_positive_exponent(1, 0, &options, "1.0"); - write_float_positive_exponent(1, 3, &options, "1000.0"); - write_float_positive_exponent(1, 12, &options, "1000000000000.0"); - write_float_positive_exponent(999999999999999, -14, &options, "9.99999999999999"); - write_float_positive_exponent(999999999999999, -13, &options, "99.9999999999999"); - write_float_positive_exponent(999999999999999, -12, &options, "999.999999999999"); - write_float_positive_exponent(17976931348623157, 292, &options, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); - - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build().unwrap(); + const OPTS1: Options = Options::new(); + write_float_positive_exponent(1, 0, &OPTS1, "1.0"); + write_float_positive_exponent(1, 3, &OPTS1, "1000.0"); + write_float_positive_exponent(1, 12, &OPTS1, "1000000000000.0"); + write_float_positive_exponent(999999999999999, -14, &OPTS1, "9.99999999999999"); + write_float_positive_exponent(999999999999999, -13, &OPTS1, "99.9999999999999"); + write_float_positive_exponent(999999999999999, -12, &OPTS1, "999.999999999999"); + write_float_positive_exponent(17976931348623157, 292, &OPTS1, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); + + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build_strict(); write_float_positive_exponent( 1, 0, - &options, + &OPTS2, "1.0000000000000000000000000000000000000000000000000", ); write_float_positive_exponent( 1, 3, - &options, + &OPTS2, "1000.0000000000000000000000000000000000000000000000", ); write_float_positive_exponent( 1, 12, - &options, + &OPTS2, "1000000000000.0000000000000000000000000000000000000", ); write_float_positive_exponent( 999999999999999, -14, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000", ); write_float_positive_exponent( 999999999999999, -13, - &options, + &OPTS2, "99.999999999999900000000000000000000000000000000000", ); write_float_positive_exponent( 999999999999999, -12, - &options, + &OPTS2, "999.99999999999900000000000000000000000000000000000", ); - write_float_positive_exponent(17976931348623157, 292, &options, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); - - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_positive_exponent(1, 0, &options, "1.0"); - write_float_positive_exponent(1, 3, &options, "1000.0"); - write_float_positive_exponent(1, 12, &options, "1000000000000.0"); - write_float_positive_exponent(999999999999999, -14, &options, "10.0"); - write_float_positive_exponent(999999999999999, -13, &options, "100.0"); - write_float_positive_exponent(999999999999999, -12, &options, "1000.0"); - write_float_positive_exponent(17976931348623157, 292, &options, "179770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_positive_exponent(1, 0, &options, "1"); - write_float_positive_exponent(1, 3, &options, "1000"); - write_float_positive_exponent(1, 12, &options, "1000000000000"); - write_float_positive_exponent(999999999999999, -14, &options, "9.99999999999999"); - write_float_positive_exponent(999999999999999, -13, &options, "99.9999999999999"); - write_float_positive_exponent(999999999999999, -12, &options, "999.999999999999"); - write_float_positive_exponent(17976931348623157, 292, &options, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + write_float_positive_exponent(17976931348623157, 292, &OPTS2, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); + + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_positive_exponent(1, 0, &OPTS3, "1.0"); + write_float_positive_exponent(1, 3, &OPTS3, "1000.0"); + write_float_positive_exponent(1, 12, &OPTS3, "1000000000000.0"); + write_float_positive_exponent(999999999999999, -14, &OPTS3, "10.0"); + write_float_positive_exponent(999999999999999, -13, &OPTS3, "100.0"); + write_float_positive_exponent(999999999999999, -12, &OPTS3, "1000.0"); + write_float_positive_exponent(17976931348623157, 292, &OPTS3, "179770000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); + + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_positive_exponent(1, 0, &OPTS4, "1"); + write_float_positive_exponent(1, 3, &OPTS4, "1000"); + write_float_positive_exponent(1, 12, &OPTS4, "1000000000000"); + write_float_positive_exponent(999999999999999, -14, &OPTS4, "9.99999999999999"); + write_float_positive_exponent(999999999999999, -13, &OPTS4, "99.9999999999999"); + write_float_positive_exponent(999999999999999, -12, &OPTS4, "999.999999999999"); + write_float_positive_exponent(17976931348623157, 292, &OPTS4, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } fn write_float_negative_exponent(mant: u64, exp: i32, options: &Options, expected: &str) { @@ -446,73 +446,73 @@ fn write_float_negative_exponent(mant: u64, exp: i32, options: &Options, expecte #[test] fn write_float_negative_exponent_test() { - let options = Options::new(); - write_float_negative_exponent(1, -1, &options, "0.1"); - write_float_negative_exponent(1, -3, &options, "0.001"); - write_float_negative_exponent(1, -12, &options, "0.000000000001"); - write_float_negative_exponent(999999999999999, -17, &options, "0.00999999999999999"); - write_float_negative_exponent(999999999999999, -16, &options, "0.0999999999999999"); - write_float_negative_exponent(999999999999999, -15, &options, "0.999999999999999"); - write_float_negative_exponent(22250738585072014, -324, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); - - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build().unwrap(); + const OPTS1: Options = Options::new(); + write_float_negative_exponent(1, -1, &OPTS1, "0.1"); + write_float_negative_exponent(1, -3, &OPTS1, "0.001"); + write_float_negative_exponent(1, -12, &OPTS1, "0.000000000001"); + write_float_negative_exponent(999999999999999, -17, &OPTS1, "0.00999999999999999"); + write_float_negative_exponent(999999999999999, -16, &OPTS1, "0.0999999999999999"); + write_float_negative_exponent(999999999999999, -15, &OPTS1, "0.999999999999999"); + write_float_negative_exponent(22250738585072014, -324, &OPTS1, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); + + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build_strict(); write_float_negative_exponent( 1, -1, - &options, + &OPTS2, "0.10000000000000000000000000000000000000000000000000", ); write_float_negative_exponent( 1, -3, - &options, + &OPTS2, "0.0010000000000000000000000000000000000000000000000000", ); write_float_negative_exponent( 1, -12, - &options, + &OPTS2, "0.0000000000010000000000000000000000000000000000000000000000000", ); write_float_negative_exponent( 999999999999999, -17, - &options, + &OPTS2, "0.0099999999999999900000000000000000000000000000000000", ); write_float_negative_exponent( 999999999999999, -16, - &options, + &OPTS2, "0.099999999999999900000000000000000000000000000000000", ); write_float_negative_exponent( 999999999999999, -15, - &options, + &OPTS2, "0.99999999999999900000000000000000000000000000000000", ); - write_float_negative_exponent(22250738585072014, -324, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014000000000000000000000000000000000"); - - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_negative_exponent(1, -1, &options, "0.1"); - write_float_negative_exponent(1, -3, &options, "0.001"); - write_float_negative_exponent(1, -12, &options, "0.000000000001"); - write_float_negative_exponent(999999999999999, -17, &options, "0.01"); - write_float_negative_exponent(999999999999999, -16, &options, "0.1"); - write_float_negative_exponent(999999999999999, -15, &options, "1.0"); - write_float_negative_exponent(22250738585072014, -324, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022251"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_negative_exponent(1, -1, &options, "0.1"); - write_float_negative_exponent(1, -3, &options, "0.001"); - write_float_negative_exponent(1, -12, &options, "0.000000000001"); - write_float_negative_exponent(999999999999999, -17, &options, "0.00999999999999999"); - write_float_negative_exponent(999999999999999, -16, &options, "0.0999999999999999"); - write_float_negative_exponent(999999999999999, -15, &options, "0.999999999999999"); - write_float_negative_exponent(22250738585072014, -324, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); + write_float_negative_exponent(22250738585072014, -324, &OPTS2, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014000000000000000000000000000000000"); + + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_negative_exponent(1, -1, &OPTS3, "0.1"); + write_float_negative_exponent(1, -3, &OPTS3, "0.001"); + write_float_negative_exponent(1, -12, &OPTS3, "0.000000000001"); + write_float_negative_exponent(999999999999999, -17, &OPTS3, "0.01"); + write_float_negative_exponent(999999999999999, -16, &OPTS3, "0.1"); + write_float_negative_exponent(999999999999999, -15, &OPTS3, "1.0"); + write_float_negative_exponent(22250738585072014, -324, &OPTS3, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022251"); + + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_negative_exponent(1, -1, &OPTS4, "0.1"); + write_float_negative_exponent(1, -3, &OPTS4, "0.001"); + write_float_negative_exponent(1, -12, &OPTS4, "0.000000000001"); + write_float_negative_exponent(999999999999999, -17, &OPTS4, "0.00999999999999999"); + write_float_negative_exponent(999999999999999, -16, &OPTS4, "0.0999999999999999"); + write_float_negative_exponent(999999999999999, -15, &OPTS4, "0.999999999999999"); + write_float_negative_exponent(22250738585072014, -324, &OPTS4, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); } // Test data for roundtrips. @@ -594,59 +594,59 @@ fn write_float(f: T, options: &Options, expecte #[test] fn f32_test() { - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float::<_, DECIMAL>(0.0f32, &options, "0"); - write_float::<_, DECIMAL>(1.0f32, &options, "1"); - write_float::<_, DECIMAL>(10.0f32, &options, "10"); - write_float::<_, DECIMAL>(10.0f32, &options, "10"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f32, &options, "1.2345679"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f32, &options, "12.345679"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f32, &options, "123.45679"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f32, &options, "1234.5679"); - write_float::<_, DECIMAL>(2.3786281e+38f32, &options, "2.3786281e38"); + const OPTS1: Options = Options::builder().trim_floats(true).build_strict(); + write_float::<_, DECIMAL>(0.0f32, &OPTS1, "0"); + write_float::<_, DECIMAL>(1.0f32, &OPTS1, "1"); + write_float::<_, DECIMAL>(10.0f32, &OPTS1, "10"); + write_float::<_, DECIMAL>(10.0f32, &OPTS1, "10"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f32, &OPTS1, "1.2345679"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f32, &OPTS1, "12.345679"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f32, &OPTS1, "123.45679"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f32, &OPTS1, "1234.5679"); + write_float::<_, DECIMAL>(2.3786281e+38f32, &OPTS1, "2.3786281e38"); - let options = Options::new(); - write_float::<_, DECIMAL>(2.3786281e+38f32, &options, "2.3786281e38"); + const OPTS2: Options = Options::new(); + write_float::<_, DECIMAL>(2.3786281e+38f32, &OPTS2, "2.3786281e38"); } #[test] fn f32_errors_test() { // Errors discovered via quickcheck. - let options = Options::new(); - write_float::<_, DECIMAL>(0.0f32, &options, "0.0"); - write_float::<_, DECIMAL>(1073741800.0f32, &options, "1073741800.0"); - write_float::<_, DECIMAL>(1610612700.0f32, &options, "1610612700.0"); - write_float::<_, DECIMAL>(1879048200.0f32, &options, "1879048200.0"); - write_float::<_, DECIMAL>(2013265900.0f32, &options, "2013265900.0"); - write_float::<_, DECIMAL>(2080374800.0f32, &options, "2080374800.0"); - write_float::<_, DECIMAL>(2113929200.0f32, &options, "2113929200.0"); - write_float::<_, DECIMAL>(2130706400.0f32, &options, "2130706400.0"); - write_float::<_, DECIMAL>(2139095000.0f32, &options, "2139095000.0"); - write_float::<_, DECIMAL>(2143289300.0f32, &options, "2143289300.0"); - write_float::<_, DECIMAL>(2145386500.0f32, &options, "2145386500.0"); - write_float::<_, DECIMAL>(2146435100.0f32, &options, "2146435100.0"); - write_float::<_, DECIMAL>(2146959400.0f32, &options, "2146959400.0"); - write_float::<_, DECIMAL>(2147221500.0f32, &options, "2147221500.0"); - write_float::<_, DECIMAL>(2147352600.0f32, &options, "2147352600.0"); - write_float::<_, DECIMAL>(2147418100.0f32, &options, "2147418100.0"); - write_float::<_, DECIMAL>(2147450900.0f32, &options, "2147450900.0"); - write_float::<_, DECIMAL>(2147467300.0f32, &options, "2147467300.0"); - write_float::<_, DECIMAL>(2147475500.0f32, &options, "2147475500.0"); - write_float::<_, DECIMAL>(2147479600.0f32, &options, "2147479600.0"); - write_float::<_, DECIMAL>(2147481600.0f32, &options, "2147481600.0"); - write_float::<_, DECIMAL>(2147482600.0f32, &options, "2147482600.0"); - write_float::<_, DECIMAL>(2147483100.0f32, &options, "2147483100.0"); - write_float::<_, DECIMAL>(2147483400.0f32, &options, "2147483400.0"); - write_float::<_, DECIMAL>(2147483500.0f32, &options, "2147483500.0"); - write_float::<_, DECIMAL>(2147483600.0f32, &options, "2147483600.0"); + const OPTIONS: Options = Options::new(); + write_float::<_, DECIMAL>(0.0f32, &OPTIONS, "0.0"); + write_float::<_, DECIMAL>(1073741800.0f32, &OPTIONS, "1073741800.0"); + write_float::<_, DECIMAL>(1610612700.0f32, &OPTIONS, "1610612700.0"); + write_float::<_, DECIMAL>(1879048200.0f32, &OPTIONS, "1879048200.0"); + write_float::<_, DECIMAL>(2013265900.0f32, &OPTIONS, "2013265900.0"); + write_float::<_, DECIMAL>(2080374800.0f32, &OPTIONS, "2080374800.0"); + write_float::<_, DECIMAL>(2113929200.0f32, &OPTIONS, "2113929200.0"); + write_float::<_, DECIMAL>(2130706400.0f32, &OPTIONS, "2130706400.0"); + write_float::<_, DECIMAL>(2139095000.0f32, &OPTIONS, "2139095000.0"); + write_float::<_, DECIMAL>(2143289300.0f32, &OPTIONS, "2143289300.0"); + write_float::<_, DECIMAL>(2145386500.0f32, &OPTIONS, "2145386500.0"); + write_float::<_, DECIMAL>(2146435100.0f32, &OPTIONS, "2146435100.0"); + write_float::<_, DECIMAL>(2146959400.0f32, &OPTIONS, "2146959400.0"); + write_float::<_, DECIMAL>(2147221500.0f32, &OPTIONS, "2147221500.0"); + write_float::<_, DECIMAL>(2147352600.0f32, &OPTIONS, "2147352600.0"); + write_float::<_, DECIMAL>(2147418100.0f32, &OPTIONS, "2147418100.0"); + write_float::<_, DECIMAL>(2147450900.0f32, &OPTIONS, "2147450900.0"); + write_float::<_, DECIMAL>(2147467300.0f32, &OPTIONS, "2147467300.0"); + write_float::<_, DECIMAL>(2147475500.0f32, &OPTIONS, "2147475500.0"); + write_float::<_, DECIMAL>(2147479600.0f32, &OPTIONS, "2147479600.0"); + write_float::<_, DECIMAL>(2147481600.0f32, &OPTIONS, "2147481600.0"); + write_float::<_, DECIMAL>(2147482600.0f32, &OPTIONS, "2147482600.0"); + write_float::<_, DECIMAL>(2147483100.0f32, &OPTIONS, "2147483100.0"); + write_float::<_, DECIMAL>(2147483400.0f32, &OPTIONS, "2147483400.0"); + write_float::<_, DECIMAL>(2147483500.0f32, &OPTIONS, "2147483500.0"); + write_float::<_, DECIMAL>(2147483600.0f32, &OPTIONS, "2147483600.0"); } #[test] fn f32_roundtrip_test() { - let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + let mut buffer: [u8; BUFFER_SIZE] = [b'\x00'; BUFFER_SIZE]; + const OPTIONS: Options = Options::builder().build_strict(); for &float in F32_DATA.iter() { - let count = algorithm::write_float::<_, DECIMAL>(float, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(float, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); assert_eq!(roundtrip, Ok(float)); @@ -655,75 +655,72 @@ fn f32_roundtrip_test() { #[test] fn f64_test() { - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0"); - write_float::<_, DECIMAL>(1.0f64, &options, "1"); - write_float::<_, DECIMAL>(10.0f64, &options, "10"); - write_float::<_, DECIMAL>(10.0f64, &options, "10"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.2345678901234567"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &options, "12.345678901234567"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &options, "123.45678901234568"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &options, "1234.567890123457"); - write_float::<_, DECIMAL>(1.5f64, &options, "1.5"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1e-17"); - write_float::<_, DECIMAL>(9.99999999999999e-16f64, &options, "9.99999999999999e-16"); - write_float::<_, DECIMAL>(9.99999999999999e-15f64, &options, "9.99999999999999e-15"); - write_float::<_, DECIMAL>(0.00999999999999999f64, &options, "0.00999999999999999"); - write_float::<_, DECIMAL>(0.0999999999999999f64, &options, "0.0999999999999999"); - write_float::<_, DECIMAL>(0.999999999999999f64, &options, "0.999999999999999"); - write_float::<_, DECIMAL>(9.99999999999999f64, &options, "9.99999999999999"); - write_float::<_, DECIMAL>(99.9999999999999f64, &options, "99.9999999999999"); - write_float::<_, DECIMAL>(999.999999999999f64, &options, "999.999999999999"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000"); - write_float::<_, DECIMAL>(1.7976931348623157e308f64, &options, "1.7976931348623157e308"); - write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &options, "2.2250738585072014e-308"); - - let options = Options::builder() + const TRIM: Options = Options::builder().trim_floats(true).build_strict(); + write_float::<_, DECIMAL>(0.0f64, &TRIM, "0"); + write_float::<_, DECIMAL>(1.0f64, &TRIM, "1"); + write_float::<_, DECIMAL>(10.0f64, &TRIM, "10"); + write_float::<_, DECIMAL>(10.0f64, &TRIM, "10"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &TRIM, "1.2345678901234567"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &TRIM, "12.345678901234567"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &TRIM, "123.45678901234568"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &TRIM, "1234.567890123457"); + write_float::<_, DECIMAL>(1.5f64, &TRIM, "1.5"); + write_float::<_, DECIMAL>(1.0e-17f64, &TRIM, "1e-17"); + write_float::<_, DECIMAL>(9.99999999999999e-16f64, &TRIM, "9.99999999999999e-16"); + write_float::<_, DECIMAL>(9.99999999999999e-15f64, &TRIM, "9.99999999999999e-15"); + write_float::<_, DECIMAL>(0.00999999999999999f64, &TRIM, "0.00999999999999999"); + write_float::<_, DECIMAL>(0.0999999999999999f64, &TRIM, "0.0999999999999999"); + write_float::<_, DECIMAL>(0.999999999999999f64, &TRIM, "0.999999999999999"); + write_float::<_, DECIMAL>(9.99999999999999f64, &TRIM, "9.99999999999999"); + write_float::<_, DECIMAL>(99.9999999999999f64, &TRIM, "99.9999999999999"); + write_float::<_, DECIMAL>(999.999999999999f64, &TRIM, "999.999999999999"); + write_float::<_, DECIMAL>(1000.0f64, &TRIM, "1000"); + write_float::<_, DECIMAL>(1.7976931348623157e308f64, &TRIM, "1.7976931348623157e308"); + write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &TRIM, "2.2250738585072014e-308"); + + const MIN_DIGITS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(50)) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, DECIMAL>(1.0e17f64, &options, "1e17"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1e-17"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000"); + .build_strict(); + write_float::<_, DECIMAL>(1.0e17f64, &MIN_DIGITS, "1e17"); + write_float::<_, DECIMAL>(1.0e-17f64, &MIN_DIGITS, "1e-17"); + write_float::<_, DECIMAL>(1000.0f64, &MIN_DIGITS, "1000"); write_float::<_, DECIMAL>( 9.99999999999999e16f64, - &options, + &MIN_DIGITS, "9.9999999999999900000000000000000000000000000000000e16", ); write_float::<_, DECIMAL>( 9.99999999999999e-16f64, - &options, + &MIN_DIGITS, "9.9999999999999900000000000000000000000000000000000e-16", ); - let truncate = Options::builder() + const TRUNCATE: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Round) - .build() - .unwrap(); + .build_strict(); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &truncate, "1.234"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &round, "1.235"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &truncate, "12.34"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &round, "12.35"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &truncate, "123.4"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &round, "123.5"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &truncate, "1234.0"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &round, "1235.0"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &TRUNCATE, "1.234"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &ROUND, "1.235"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &TRUNCATE, "12.34"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &ROUND, "12.35"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &TRUNCATE, "123.4"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &ROUND, "123.5"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &TRUNCATE, "1234.0"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &ROUND, "1235.0"); } #[test] fn f64_roundtrip_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); for &float in F64_DATA.iter() { - let count = algorithm::write_float::<_, DECIMAL>(float, &mut buffer, &options); + let count = algorithm::write_float::<_, DECIMAL>(float, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); assert_eq!(roundtrip, Ok(float)); diff --git a/lexical-write-float/tests/api_tests.rs b/lexical-write-float/tests/api_tests.rs index e5e22140..45ed9fd2 100644 --- a/lexical-write-float/tests/api_tests.rs +++ b/lexical-write-float/tests/api_tests.rs @@ -33,12 +33,12 @@ fn special_test() { let actual = unsafe { std::str::from_utf8_unchecked(f64::INFINITY.to_lexical(&mut buffer)) }; assert_eq!(actual, "inf"); - let options = - Options::builder().nan_string(Some(b"nan")).inf_string(Some(b"Infinity")).build().unwrap(); - let bytes = f64::NAN.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options); + const OPTIONS: Options = + Options::builder().nan_string(Some(b"nan")).inf_string(Some(b"Infinity")).build_strict(); + let bytes = f64::NAN.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(bytes) }; assert_eq!(actual, "nan"); - let bytes = f64::INFINITY.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options); + let bytes = f64::INFINITY.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(bytes) }; assert_eq!(actual, "Infinity"); } @@ -47,16 +47,16 @@ fn special_test() { #[should_panic] fn invalid_nan_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().nan_string(None).build().unwrap(); - f64::NAN.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options); + const OPTIONS: Options = Options::builder().nan_string(None).build_strict(); + f64::NAN.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS); } #[test] #[should_panic] fn invalid_inf_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().inf_string(None).build().unwrap(); - f64::INFINITY.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options); + const OPTIONS: Options = Options::builder().inf_string(None).build_strict(); + f64::INFINITY.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS); } #[test] @@ -70,7 +70,7 @@ fn hex_test() { .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const HEX_OPTIONS: Options = Options::builder().exponent(b'^').build_unchecked(); let mut buffer = [b'\x00'; BUFFER_SIZE]; diff --git a/lexical-write-float/tests/binary_tests.rs b/lexical-write-float/tests/binary_tests.rs index 29c1004b..93c7f36c 100644 --- a/lexical-write-float/tests/binary_tests.rs +++ b/lexical-write-float/tests/binary_tests.rs @@ -18,28 +18,28 @@ const BASE2_2_4: u128 = NumberFormatBuilder::new() .mantissa_radix(2) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(4)) - .build(); + .build_strict(); const BASE4_4_8: u128 = NumberFormatBuilder::new() .mantissa_radix(4) .exponent_base(num::NonZeroU8::new(4)) .exponent_radix(num::NonZeroU8::new(8)) - .build(); + .build_strict(); const BASE4_2_32: u128 = NumberFormatBuilder::new() .mantissa_radix(4) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(32)) - .build(); + .build_strict(); const BASE4_8_4: u128 = NumberFormatBuilder::new() .mantissa_radix(4) .exponent_base(num::NonZeroU8::new(8)) .exponent_radix(num::NonZeroU8::new(4)) - .build(); + .build_strict(); const BASE32_2_32: u128 = NumberFormatBuilder::new() .mantissa_radix(32) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(32)) - .build(); -const HEX_OPTIONS: Options = Options::builder().exponent(b'^').build_unchecked(); + .build_strict(); +const HEX_OPTIONS: Options = Options::builder().exponent(b'^').build_strict(); #[test] fn fast_log2_test() { @@ -133,28 +133,26 @@ fn scale_sci_exp_test() { #[test] fn truncate_and_round_test() { - let truncate = Options::builder() + const TRUNCATE: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Round) - .build() - .unwrap(); + .build_strict(); // Above halfway - assert_eq!(binary::truncate_and_round(6602499140956772u64, 2, &round), (12, 53)); - assert_eq!(binary::truncate_and_round(6602499140956772u64, 2, &truncate), (11, 53)); + assert_eq!(binary::truncate_and_round(6602499140956772u64, 2, &ROUND), (12, 53)); + assert_eq!(binary::truncate_and_round(6602499140956772u64, 2, &TRUNCATE), (11, 53)); // At halfway - assert_eq!(binary::truncate_and_round(6473924464345088u64, 2, &round), (12, 53)); - assert_eq!(binary::truncate_and_round(6473924464345088u64, 2, &truncate), (11, 53)); + assert_eq!(binary::truncate_and_round(6473924464345088u64, 2, &ROUND), (12, 53)); + assert_eq!(binary::truncate_and_round(6473924464345088u64, 2, &TRUNCATE), (11, 53)); // Below halfway. - assert_eq!(binary::truncate_and_round(6473924464345087u64, 2, &round), (11, 53)); - assert_eq!(binary::truncate_and_round(6473924464345087u64, 2, &truncate), (11, 53)); + assert_eq!(binary::truncate_and_round(6473924464345087u64, 2, &ROUND), (11, 53)); + assert_eq!(binary::truncate_and_round(6473924464345087u64, 2, &TRUNCATE), (11, 53)); } // NOTE: This doesn't handle float rounding or truncation. @@ -183,82 +181,82 @@ fn write_float_scientific_test() { // Positive exponent // Check no formatting, binary. - let options = Options::builder().build().unwrap(); - write_float_scientific::<_, BINARY>(0.0f64, &options, "0.0e0"); - write_float_scientific::<_, BINARY>(1.0f64, &options, "1.0e0"); - write_float_scientific::<_, BINARY>(2.0f64, &options, "1.0e1"); - write_float_scientific::<_, BINARY>(0.5f64, &options, "1.0e-1"); + const OPTIONS: Options = Options::builder().build_strict(); + write_float_scientific::<_, BINARY>(0.0f64, &OPTIONS, "0.0e0"); + write_float_scientific::<_, BINARY>(1.0f64, &OPTIONS, "1.0e0"); + write_float_scientific::<_, BINARY>(2.0f64, &OPTIONS, "1.0e1"); + write_float_scientific::<_, BINARY>(0.5f64, &OPTIONS, "1.0e-1"); write_float_scientific::<_, BINARY>( 0.2345678901234567890e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e100", ); write_float_scientific::<_, BINARY>( 0.1172839450617284e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e11", ); write_float_scientific::<_, BINARY>( 0.0586419725308642e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e10", ); write_float_scientific::<_, BINARY>( 0.0293209862654321e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e1", ); write_float_scientific::<_, BINARY>( 0.01466049313271605e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e0", ); // Check no formatting, base 4. - write_float_scientific::<_, BASE4>(0.0f64, &options, "0.0e0"); - write_float_scientific::<_, BASE4>(1.0f64, &options, "1.0e0"); - write_float_scientific::<_, BASE4>(2.0f64, &options, "2.0e0"); - write_float_scientific::<_, BASE4>(0.5f64, &options, "2.0e-1"); + write_float_scientific::<_, BASE4>(0.0f64, &OPTIONS, "0.0e0"); + write_float_scientific::<_, BASE4>(1.0f64, &OPTIONS, "1.0e0"); + write_float_scientific::<_, BASE4>(2.0f64, &OPTIONS, "2.0e0"); + write_float_scientific::<_, BASE4>(0.5f64, &OPTIONS, "2.0e-1"); write_float_scientific::<_, BASE4>( 0.2345678901234567890e2f64, - &options, + &OPTIONS, "1.1313103300013332310302121e2", ); write_float_scientific::<_, BASE4>( 0.1172839450617284e2f64, - &options, + &OPTIONS, "2.3232213200033331221210302e1", ); write_float_scientific::<_, BASE4>( 0.0586419725308642e2f64, - &options, + &OPTIONS, "1.1313103300013332310302121e1", ); write_float_scientific::<_, BASE4>( 0.0293209862654321e2f64, - &options, + &OPTIONS, "2.3232213200033331221210302e0", ); write_float_scientific::<_, BASE4>( 0.01466049313271605e2f64, - &options, + &OPTIONS, "1.1313103300013332310302121e0", ); // Check no formatting, octal. - write_float_scientific::<_, OCTAL>(0.0f64, &options, "0.0e0"); - write_float_scientific::<_, OCTAL>(1.0f64, &options, "1.0e0"); - write_float_scientific::<_, OCTAL>(2.0f64, &options, "2.0e0"); - write_float_scientific::<_, OCTAL>(0.5f64, &options, "4.0e-1"); + write_float_scientific::<_, OCTAL>(0.0f64, &OPTIONS, "0.0e0"); + write_float_scientific::<_, OCTAL>(1.0f64, &OPTIONS, "1.0e0"); + write_float_scientific::<_, OCTAL>(2.0f64, &OPTIONS, "2.0e0"); + write_float_scientific::<_, OCTAL>(0.5f64, &OPTIONS, "4.0e-1"); write_float_scientific::<_, OCTAL>( 0.2345678901234567890e2f64, - &options, + &OPTIONS, "2.73517003773231144e1", ); - write_float_scientific::<_, OCTAL>(0.1172839450617284e2f64, &options, "1.35647401775514462e1"); - write_float_scientific::<_, OCTAL>(0.0586419725308642e2f64, &options, "5.6723600776646231e0"); - write_float_scientific::<_, OCTAL>(0.0293209862654321e2f64, &options, "2.73517003773231144e0"); - write_float_scientific::<_, OCTAL>(0.01466049313271605e2f64, &options, "1.35647401775514462e0"); + write_float_scientific::<_, OCTAL>(0.1172839450617284e2f64, &OPTIONS, "1.35647401775514462e1"); + write_float_scientific::<_, OCTAL>(0.0586419725308642e2f64, &OPTIONS, "5.6723600776646231e0"); + write_float_scientific::<_, OCTAL>(0.0293209862654321e2f64, &OPTIONS, "2.73517003773231144e0"); + write_float_scientific::<_, OCTAL>(0.01466049313271605e2f64, &OPTIONS, "1.35647401775514462e0"); // Check no formatting, hexadecimal. write_float_scientific::<_, HEX>(0.0f64, &HEX_OPTIONS, "0.0^0"); @@ -287,67 +285,67 @@ fn write_float_scientific_test() { // Check no formatting, binary. write_float_scientific::<_, BINARY>( 0.2345678901234567890f64, - &options, + &OPTIONS, "1.11100000011001010010000101000110001011001111110111e-11", ); write_float_scientific::<_, BINARY>( 0.1172839450617284f64, - &options, + &OPTIONS, "1.11100000011001010010000101000110001011001111110111e-100", ); write_float_scientific::<_, BINARY>( 0.0586419725308642f64, - &options, + &OPTIONS, "1.11100000011001010010000101000110001011001111110111e-101", ); write_float_scientific::<_, BINARY>( 0.0293209862654321f64, - &options, + &OPTIONS, "1.11100000011001010010000101000110001011001111110111e-110", ); write_float_scientific::<_, BINARY>( 0.01466049313271605f64, - &options, + &OPTIONS, "1.11100000011001010010000101000110001011001111110111e-111", ); // Check no formatting, base 4. write_float_scientific::<_, BASE4>( 0.2345678901234567890f64, - &options, + &OPTIONS, "3.3000302210022030112133232e-2", ); write_float_scientific::<_, BASE4>( 0.1172839450617284f64, - &options, + &OPTIONS, "1.3200121102011012023033313e-2", ); write_float_scientific::<_, BASE4>( 0.0586419725308642f64, - &options, + &OPTIONS, "3.3000302210022030112133232e-3", ); write_float_scientific::<_, BASE4>( 0.0293209862654321f64, - &options, + &OPTIONS, "1.3200121102011012023033313e-3", ); write_float_scientific::<_, BASE4>( 0.01466049313271605f64, - &options, + &OPTIONS, "3.3000302210022030112133232e-10", ); // Check no formatting, octal. write_float_scientific::<_, OCTAL>( 0.2345678901234567890f64, - &options, + &OPTIONS, "1.70062441214263756e-1", ); - write_float_scientific::<_, OCTAL>(0.1172839450617284f64, &options, "7.4031220506131767e-2"); - write_float_scientific::<_, OCTAL>(0.0586419725308642f64, &options, "3.60145102430547734e-2"); - write_float_scientific::<_, OCTAL>(0.0293209862654321f64, &options, "1.70062441214263756e-2"); - write_float_scientific::<_, OCTAL>(0.01466049313271605f64, &options, "7.4031220506131767e-3"); + write_float_scientific::<_, OCTAL>(0.1172839450617284f64, &OPTIONS, "7.4031220506131767e-2"); + write_float_scientific::<_, OCTAL>(0.0586419725308642f64, &OPTIONS, "3.60145102430547734e-2"); + write_float_scientific::<_, OCTAL>(0.0293209862654321f64, &OPTIONS, "1.70062441214263756e-2"); + write_float_scientific::<_, OCTAL>(0.01466049313271605f64, &OPTIONS, "7.4031220506131767e-3"); // Check no formatting, hexadecimal. write_float_scientific::<_, HEX>(0.2345678901234567890f64, &HEX_OPTIONS, "3.C0CA428C59FB8^-1"); @@ -366,79 +364,78 @@ fn write_float_scientific_test() { // Different exponent radix. write_float_scientific::<_, BASE2_2_4>( 0.2345678901234567890e2f64, - &options, + &OPTIONS, "1.01110111010011110000000111111110110100110010011001e10", ); write_float_scientific::<_, BASE4_4_8>( 0.2345678901234567890e2f64, - &options, + &OPTIONS, "1.1313103300013332310302121e2", ); // Check no formatting, f32, binary. write_float_scientific::<_, BINARY>( 1.2345678901234567890f32, - &options, + &OPTIONS, "1.0011110000001100101001e0", ); write_float_scientific::<_, BINARY>( 3.2345678901234567890f32, - &options, + &OPTIONS, "1.10011110000001100101001e1", ); - write_float_scientific::<_, BINARY>(1f32, &options, "1.0e0"); + write_float_scientific::<_, BINARY>(1f32, &OPTIONS, "1.0e0"); write_float_scientific::<_, BINARY>( 0.2345678901234567890f32, - &options, + &OPTIONS, "1.11100000011001010010001e-11", ); write_float_scientific::<_, BINARY>( 0.7345678901234567890f32, - &options, + &OPTIONS, "1.011110000001100101001e-1", ); - write_float_scientific::<_, BINARY>(1.4e-45f32, &options, "1.0e-10010101"); + write_float_scientific::<_, BINARY>(1.4e-45f32, &OPTIONS, "1.0e-10010101"); write_float_scientific::<_, BINARY>( 3.4028234664e38f32, - &options, + &OPTIONS, "1.11111111111111111111111e1111111", ); // Check with a minimum number of digits. - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_scientific::<_, BINARY>(0.0f64, &options, "0.0000e0"); - write_float_scientific::<_, BINARY>(1.0f64, &options, "1.0000e0"); - write_float_scientific::<_, BINARY>(2.0f64, &options, "1.0000e1"); - write_float_scientific::<_, BINARY>(0.5f64, &options, "1.0000e-1"); + const MIN_DIGITS: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_scientific::<_, BINARY>(0.0f64, &MIN_DIGITS, "0.0000e0"); + write_float_scientific::<_, BINARY>(1.0f64, &MIN_DIGITS, "1.0000e0"); + write_float_scientific::<_, BINARY>(2.0f64, &MIN_DIGITS, "1.0000e1"); + write_float_scientific::<_, BINARY>(0.5f64, &MIN_DIGITS, "1.0000e-1"); write_float_scientific::<_, BASE4>( 0.2345678901234567890e2f64, - &options, + &MIN_DIGITS, "1.1313103300013332310302121e2", ); - let options = Options::builder() + const TRIM_MIN_DIGITS: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float_scientific::<_, BINARY>(0.0f64, &options, "0e0"); - write_float_scientific::<_, BINARY>(1.0f64, &options, "1e0"); - write_float_scientific::<_, BINARY>(2.0f64, &options, "1e1"); - write_float_scientific::<_, BINARY>(0.5f64, &options, "1e-1"); + .build_strict(); + write_float_scientific::<_, BINARY>(0.0f64, &TRIM_MIN_DIGITS, "0e0"); + write_float_scientific::<_, BINARY>(1.0f64, &TRIM_MIN_DIGITS, "1e0"); + write_float_scientific::<_, BINARY>(2.0f64, &TRIM_MIN_DIGITS, "1e1"); + write_float_scientific::<_, BINARY>(0.5f64, &TRIM_MIN_DIGITS, "1e-1"); write_float_scientific::<_, BASE4>( 0.2345678901234567890e2f64, - &options, + &TRIM_MIN_DIGITS, "1.1313103300013332310302121e2", ); // Check trimming floats - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_scientific::<_, BINARY>(1f32, &options, "1e0"); - write_float_scientific::<_, BINARY>(1.4e-45f32, &options, "1e-10010101"); + const TRIM: Options = Options::builder().trim_floats(true).build_strict(); + write_float_scientific::<_, BINARY>(1f32, &TRIM, "1e0"); + write_float_scientific::<_, BINARY>(1.4e-45f32, &TRIM, "1e-10010101"); write_float_scientific::<_, BINARY>( 1.2345678901234567890f32, - &options, + &TRIM, "1.0011110000001100101001e0", ); } @@ -474,84 +471,84 @@ fn write_float_negative_exponent_test() { // Negative exponent // Check no formatting, binary. - let options = Options::builder().build().unwrap(); + const OPTS1: Options = Options::builder().build_strict(); write_float_negative_exponent::<_, BINARY>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.00111100000011001010010000101000110001011001111110111", ); write_float_negative_exponent::<_, BINARY>( 0.1172839450617284f64, - &options, + &OPTS1, "0.000111100000011001010010000101000110001011001111110111", ); write_float_negative_exponent::<_, BINARY>( 0.0586419725308642f64, - &options, + &OPTS1, "0.0000111100000011001010010000101000110001011001111110111", ); write_float_negative_exponent::<_, BINARY>( 0.0293209862654321f64, - &options, + &OPTS1, "0.00000111100000011001010010000101000110001011001111110111", ); write_float_negative_exponent::<_, BINARY>( 0.01466049313271605f64, - &options, + &OPTS1, "0.000000111100000011001010010000101000110001011001111110111", ); // Check no formatting, base 4. write_float_negative_exponent::<_, BASE4>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.033000302210022030112133232", ); write_float_negative_exponent::<_, BASE4>( 0.1172839450617284f64, - &options, + &OPTS1, "0.013200121102011012023033313", ); write_float_negative_exponent::<_, BASE4>( 0.0586419725308642f64, - &options, + &OPTS1, "0.0033000302210022030112133232", ); write_float_negative_exponent::<_, BASE4>( 0.0293209862654321f64, - &options, + &OPTS1, "0.0013200121102011012023033313", ); write_float_negative_exponent::<_, BASE4>( 0.01466049313271605f64, - &options, + &OPTS1, "0.00033000302210022030112133232", ); // Check no formatting, octal. write_float_negative_exponent::<_, OCTAL>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.170062441214263756", ); write_float_negative_exponent::<_, OCTAL>( 0.1172839450617284f64, - &options, + &OPTS1, "0.074031220506131767", ); write_float_negative_exponent::<_, OCTAL>( 0.0586419725308642f64, - &options, + &OPTS1, "0.0360145102430547734", ); write_float_negative_exponent::<_, OCTAL>( 0.0293209862654321f64, - &options, + &OPTS1, "0.0170062441214263756", ); write_float_negative_exponent::<_, OCTAL>( 0.01466049313271605f64, - &options, + &OPTS1, "0.0074031220506131767", ); @@ -612,71 +609,70 @@ fn write_float_negative_exponent_test() { // Different exponent radix. write_float_negative_exponent::<_, BASE2_2_4>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.00111100000011001010010000101000110001011001111110111", ); write_float_negative_exponent::<_, BASE4_2_32>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.033000302210022030112133232", ); write_float_negative_exponent::<_, BASE4_4_8>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.033000302210022030112133232", ); write_float_negative_exponent::<_, BASE4_8_4>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.033000302210022030112133232", ); write_float_negative_exponent::<_, BASE32_2_32>( 0.2345678901234567890f64, - &options, + &OPTS1, "0.7G6A8A65JUS", ); // Check no formatting, f32, binary. write_float_negative_exponent::<_, BINARY>( 0.2345678901234567890f32, - &options, + &OPTS1, "0.00111100000011001010010001", ); write_float_negative_exponent::<_, BINARY>( 0.7345678901234567890f32, - &options, + &OPTS1, "0.1011110000001100101001", ); - write_float_negative_exponent::<_, BINARY>(1.4e-45f32, &options, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + write_float_negative_exponent::<_, BINARY>(1.4e-45f32, &OPTS1, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); // Check with a minimum number of digits. - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_negative_exponent::<_, BINARY>(0.5f64, &options, "0.10000"); + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_negative_exponent::<_, BINARY>(0.5f64, &OPTS2, "0.10000"); write_float_negative_exponent::<_, BASE4>( 0.2345678901234567890f64, - &options, + &OPTS2, "0.033000302210022030112133232", ); - let options = Options::builder() + const OPTS3: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float_negative_exponent::<_, BINARY>(0.5f64, &options, "0.10000"); + .build_strict(); + write_float_negative_exponent::<_, BINARY>(0.5f64, &OPTS3, "0.10000"); write_float_negative_exponent::<_, BASE4>( 0.2345678901234567890f64, - &options, + &OPTS3, "0.033000302210022030112133232", ); // Check trimming floats does nothing. - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_negative_exponent::<_, BINARY>(0.5f64, &options, "0.1"); + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_negative_exponent::<_, BINARY>(0.5f64, &OPTS4, "0.1"); write_float_negative_exponent::<_, BASE4>( 0.2345678901234567890f64, - &options, + &OPTS4, "0.033000302210022030112133232", ); } @@ -715,93 +711,93 @@ fn write_float_positive_exponent_test() { // Positive exponent // Check no formatting, binary. - let options = Options::builder().build().unwrap(); - write_float_positive_exponent::<_, BINARY>(0.0f64, &options, "0.0"); - write_float_positive_exponent::<_, BINARY>(1.0f64, &options, "1.0"); - write_float_positive_exponent::<_, BINARY>(2.0f64, &options, "10.0"); + const OPTS1: Options = Options::builder().build_strict(); + write_float_positive_exponent::<_, BINARY>(0.0f64, &OPTS1, "0.0"); + write_float_positive_exponent::<_, BINARY>(1.0f64, &OPTS1, "1.0"); + write_float_positive_exponent::<_, BINARY>(2.0f64, &OPTS1, "10.0"); write_float_positive_exponent::<_, BINARY>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "10111.0111010011110000000111111110110100110010011001", ); write_float_positive_exponent::<_, BINARY>( 0.1172839450617284e2f64, - &options, + &OPTS1, "1011.10111010011110000000111111110110100110010011001", ); write_float_positive_exponent::<_, BINARY>( 0.0586419725308642e2f64, - &options, + &OPTS1, "101.110111010011110000000111111110110100110010011001", ); write_float_positive_exponent::<_, BINARY>( 0.0293209862654321e2f64, - &options, + &OPTS1, "10.1110111010011110000000111111110110100110010011001", ); write_float_positive_exponent::<_, BINARY>( 0.01466049313271605e2f64, - &options, + &OPTS1, "1.01110111010011110000000111111110110100110010011001", ); // Check no formatting, base 4. - write_float_positive_exponent::<_, BASE4>(0.0f64, &options, "0.0"); - write_float_positive_exponent::<_, BASE4>(1.0f64, &options, "1.0"); - write_float_positive_exponent::<_, BASE4>(2.0f64, &options, "2.0"); + write_float_positive_exponent::<_, BASE4>(0.0f64, &OPTS1, "0.0"); + write_float_positive_exponent::<_, BASE4>(1.0f64, &OPTS1, "1.0"); + write_float_positive_exponent::<_, BASE4>(2.0f64, &OPTS1, "2.0"); write_float_positive_exponent::<_, BASE4>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "113.13103300013332310302121", ); write_float_positive_exponent::<_, BASE4>( 0.1172839450617284e2f64, - &options, + &OPTS1, "23.232213200033331221210302", ); write_float_positive_exponent::<_, BASE4>( 0.0586419725308642e2f64, - &options, + &OPTS1, "11.313103300013332310302121", ); write_float_positive_exponent::<_, BASE4>( 0.0293209862654321e2f64, - &options, + &OPTS1, "2.3232213200033331221210302", ); write_float_positive_exponent::<_, BASE4>( 0.01466049313271605e2f64, - &options, + &OPTS1, "1.1313103300013332310302121", ); // Check no formatting, octal. - write_float_positive_exponent::<_, OCTAL>(0.0f64, &options, "0.0"); - write_float_positive_exponent::<_, OCTAL>(1.0f64, &options, "1.0"); - write_float_positive_exponent::<_, OCTAL>(2.0f64, &options, "2.0"); + write_float_positive_exponent::<_, OCTAL>(0.0f64, &OPTS1, "0.0"); + write_float_positive_exponent::<_, OCTAL>(1.0f64, &OPTS1, "1.0"); + write_float_positive_exponent::<_, OCTAL>(2.0f64, &OPTS1, "2.0"); write_float_positive_exponent::<_, OCTAL>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "27.3517003773231144", ); write_float_positive_exponent::<_, OCTAL>( 0.1172839450617284e2f64, - &options, + &OPTS1, "13.5647401775514462", ); write_float_positive_exponent::<_, OCTAL>( 0.0586419725308642e2f64, - &options, + &OPTS1, "5.6723600776646231", ); write_float_positive_exponent::<_, OCTAL>( 0.0293209862654321e2f64, - &options, + &OPTS1, "2.73517003773231144", ); write_float_positive_exponent::<_, OCTAL>( 0.01466049313271605e2f64, - &options, + &OPTS1, "1.35647401775514462", ); @@ -868,22 +864,22 @@ fn write_float_positive_exponent_test() { // Different exponent radix. write_float_positive_exponent::<_, BASE2_2_4>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "10111.0111010011110000000111111110110100110010011001", ); write_float_positive_exponent::<_, BASE4_2_32>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "113.13103300013332310302121", ); write_float_positive_exponent::<_, BASE4_4_8>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "113.13103300013332310302121", ); write_float_positive_exponent::<_, BASE4_8_4>( 0.2345678901234567890e2f64, - &options, + &OPTS1, "113.13103300013332310302121", ); write_float_positive_exponent::<_, BASE32_2_32>( @@ -895,44 +891,43 @@ fn write_float_positive_exponent_test() { // Check no formatting, f32, binary. write_float_positive_exponent::<_, BINARY>( 0.2345678901234567890e2f32, - &options, + &OPTS1, "10111.0111010011110000001", ); write_float_positive_exponent::<_, BINARY>( 0.7345678901234567890e2f32, - &options, + &OPTS1, "1001001.011101001111", ); - write_float_positive_exponent::<_, BINARY>(3.4028234664e38f32, &options, "11111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); + write_float_positive_exponent::<_, BINARY>(3.4028234664e38f32, &OPTS1, "11111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); // Check with a minimum number of digits. - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_positive_exponent::<_, BINARY>(1.0f64, &options, "1.0000"); + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_positive_exponent::<_, BINARY>(1.0f64, &OPTS2, "1.0000"); write_float_positive_exponent::<_, BINARY>( 0.2345678901234567890e2f32, - &options, + &OPTS2, "10111.0111010011110000001", ); - let options = Options::builder() + const OPTS3: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float_positive_exponent::<_, BINARY>(1.0f64, &options, "1"); + .build_strict(); + write_float_positive_exponent::<_, BINARY>(1.0f64, &OPTS3, "1"); write_float_positive_exponent::<_, BINARY>( 0.2345678901234567890e2f32, - &options, + &OPTS3, "10111.0111010011110000001", ); // Check trimming floats works. - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_positive_exponent::<_, BINARY>(1.0f64, &options, "1"); + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_positive_exponent::<_, BINARY>(1.0f64, &OPTS4, "1"); write_float_positive_exponent::<_, BINARY>( 0.2345678901234567890e2f32, - &options, + &OPTS4, "10111.0111010011110000001", ); } @@ -950,132 +945,125 @@ where #[test] fn write_float_test() { // Check no formatting, binary, and when exponent notation is used. - let options = Options::builder().build().unwrap(); - write_float::<_, BINARY>(0.0f64, &options, "0.0"); - write_float::<_, BINARY>(1.0f64, &options, "1.0"); - write_float::<_, BINARY>(2.0f64, &options, "10.0"); - write_float::<_, BINARY>(0.5f64, &options, "0.1"); + const OPTS1: Options = Options::builder().build_strict(); + write_float::<_, BINARY>(0.0f64, &OPTS1, "0.0"); + write_float::<_, BINARY>(1.0f64, &OPTS1, "1.0"); + write_float::<_, BINARY>(2.0f64, &OPTS1, "10.0"); + write_float::<_, BINARY>(0.5f64, &OPTS1, "0.1"); write_float::<_, BINARY>( 23.45678901234567890f64, - &options, + &OPTS1, "10111.0111010011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 93.82715604938272f64, - &options, + &OPTS1, "1011101.11010011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 375.3086241975309f64, - &options, + &OPTS1, "101110111.010011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 750.6172483950618f64, - &options, + &OPTS1, "1011101110.10011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 1501.2344967901236f64, - &options, + &OPTS1, "1.01110111010011110000000111111110110100110010011001e1010", ); write_float::<_, BINARY>( 0.09162808207947531f64, - &options, + &OPTS1, "0.000101110111010011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 0.04581404103973766f64, - &options, + &OPTS1, "0.0000101110111010011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 0.02290702051986883f64, - &options, + &OPTS1, "1.01110111010011110000000111111110110100110010011001e-110", ); // Try changing the exponent limits. - let options = Options::builder() + const OPTS2: Options = Options::builder() .negative_exponent_break(num::NonZeroI32::new(-6)) .positive_exponent_break(num::NonZeroI32::new(10)) - .build() - .unwrap(); + .build_strict(); write_float::<_, BINARY>( 1501.2344967901236f64, - &options, + &OPTS2, "10111011101.0011110000000111111110110100110010011001", ); write_float::<_, BINARY>( 0.02290702051986883f64, - &options, + &OPTS2, "0.00000101110111010011110000000111111110110100110010011001", ); // Check max digits. - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float::<_, BINARY>(0.0f64, &options, "0.0"); - write_float::<_, BINARY>(1.0f64, &options, "1.0"); - write_float::<_, BINARY>(2.0f64, &options, "10.0"); - write_float::<_, BINARY>(0.5f64, &options, "0.1"); - write_float::<_, BINARY>(0.2345678901234567890f64, &options, "0.001111"); - write_float::<_, BINARY>(23.45678901234567890f64, &options, "10111.0"); - write_float::<_, BINARY>(93.82715604938272f64, &options, "1011100.0"); - write_float::<_, BINARY>(375.3086241975309f64, &options, "101110000.0"); + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float::<_, BINARY>(0.0f64, &OPTS3, "0.0"); + write_float::<_, BINARY>(1.0f64, &OPTS3, "1.0"); + write_float::<_, BINARY>(2.0f64, &OPTS3, "10.0"); + write_float::<_, BINARY>(0.5f64, &OPTS3, "0.1"); + write_float::<_, BINARY>(0.2345678901234567890f64, &OPTS3, "0.001111"); + write_float::<_, BINARY>(23.45678901234567890f64, &OPTS3, "10111.0"); + write_float::<_, BINARY>(93.82715604938272f64, &OPTS3, "1011100.0"); + write_float::<_, BINARY>(375.3086241975309f64, &OPTS3, "101110000.0"); // Check max digits and trim floats. - let options = Options::builder() + const OPTS4: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, BINARY>(0.2345678901234567890f64, &options, "0.001111"); - write_float::<_, BINARY>(23.45678901234567890f64, &options, "10111"); - write_float::<_, BINARY>(93.82715604938272f64, &options, "1011100"); - write_float::<_, BINARY>(375.3086241975309f64, &options, "101110000"); + .build_strict(); + write_float::<_, BINARY>(0.2345678901234567890f64, &OPTS4, "0.001111"); + write_float::<_, BINARY>(23.45678901234567890f64, &OPTS4, "10111"); + write_float::<_, BINARY>(93.82715604938272f64, &OPTS4, "1011100"); + write_float::<_, BINARY>(375.3086241975309f64, &OPTS4, "101110000"); // Test the round mode. - let truncate = Options::builder() + const TRUNCATE1: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND1: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Round) - .build() - .unwrap(); - write_float::<_, BINARY>(23.45678901234567890f64, &round, "11000.0"); - write_float::<_, BINARY>(23.45678901234567890f64, &truncate, "10110.0"); + .build_strict(); + write_float::<_, BINARY>(23.45678901234567890f64, &ROUND1, "11000.0"); + write_float::<_, BINARY>(23.45678901234567890f64, &TRUNCATE1, "10110.0"); - let truncate = Options::builder() + const TRUNCATE2: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(8)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND2: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(8)) .round_mode(RoundMode::Round) - .build() - .unwrap(); - - write_float::<_, BINARY>(1.2345678901234567890e0f64, &truncate, "1.001111"); - write_float::<_, BINARY>(1.2345678901234567890e0f64, &round, "1.001111"); - write_float::<_, BINARY>(1.2345678901234567890e1f64, &truncate, "1100.0101"); - write_float::<_, BINARY>(1.2345678901234567890e1f64, &round, "1100.011"); - write_float::<_, BINARY>(1.2345678901234567890e2f64, &truncate, "1111011.0"); - write_float::<_, BINARY>(1.2345678901234567890e2f64, &round, "1111011.1"); - write_float::<_, BINARY>(1.2345678901234567890e3f64, &truncate, "1.001101e1010"); - write_float::<_, BINARY>(1.2345678901234567890e3f64, &round, "1.001101e1010"); - - let truncate = Options::builder() + .build_strict(); + + write_float::<_, BINARY>(1.2345678901234567890e0f64, &TRUNCATE2, "1.001111"); + write_float::<_, BINARY>(1.2345678901234567890e0f64, &ROUND2, "1.001111"); + write_float::<_, BINARY>(1.2345678901234567890e1f64, &TRUNCATE2, "1100.0101"); + write_float::<_, BINARY>(1.2345678901234567890e1f64, &ROUND2, "1100.011"); + write_float::<_, BINARY>(1.2345678901234567890e2f64, &TRUNCATE2, "1111011.0"); + write_float::<_, BINARY>(1.2345678901234567890e2f64, &ROUND2, "1111011.1"); + write_float::<_, BINARY>(1.2345678901234567890e3f64, &TRUNCATE2, "1.001101e1010"); + write_float::<_, BINARY>(1.2345678901234567890e3f64, &ROUND2, "1.001101e1010"); + + const TRUNCATE3: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(8)) .round_mode(RoundMode::Truncate) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, BINARY>(1.2345678901234567890e2f64, &truncate, "1111011"); - write_float::<_, BINARY>(1.2345678901234567890e2f64, &round, "1111011.1"); + .build_strict(); + write_float::<_, BINARY>(1.2345678901234567890e2f64, &TRUNCATE3, "1111011"); + write_float::<_, BINARY>(1.2345678901234567890e2f64, &ROUND2, "1111011.1"); } diff --git a/lexical-write-float/tests/compact_tests.rs b/lexical-write-float/tests/compact_tests.rs index 37c0c11e..80c3f40f 100644 --- a/lexical-write-float/tests/compact_tests.rs +++ b/lexical-write-float/tests/compact_tests.rs @@ -263,193 +263,189 @@ fn write_float(f: T, options: &Options, expecte #[test] fn write_float_test() { - let options = Options::builder().build().unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0.0"); - write_float::<_, DECIMAL>(1.0f64, &options, "1.0"); - write_float::<_, DECIMAL>(1.5f64, &options, "1.5"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.2345678901234567"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1.0e-17"); - write_float::<_, DECIMAL>(9.99999999999999e-16f64, &options, "9.99999999999999e-16"); - write_float::<_, DECIMAL>(9.99999999999999e-15f64, &options, "9.99999999999999e-15"); - write_float::<_, DECIMAL>(0.00999999999999999f64, &options, "0.00999999999999999"); - write_float::<_, DECIMAL>(0.0999999999999999f64, &options, "0.0999999999999999"); - write_float::<_, DECIMAL>(0.999999999999999f64, &options, "0.999999999999999"); - write_float::<_, DECIMAL>(9.99999999999999f64, &options, "9.99999999999999"); - write_float::<_, DECIMAL>(99.9999999999999f64, &options, "99.9999999999999"); - write_float::<_, DECIMAL>(999.999999999999f64, &options, "999.999999999999"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000.0"); - write_float::<_, DECIMAL>(1.7976931348623157e308f64, &options, "1.7976931348623157e308"); - write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &options, "2.2250738585072014e-308"); - - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build().unwrap(); + const OPTS1: Options = Options::builder().build_strict(); + write_float::<_, DECIMAL>(0.0f64, &OPTS1, "0.0"); + write_float::<_, DECIMAL>(1.0f64, &OPTS1, "1.0"); + write_float::<_, DECIMAL>(1.5f64, &OPTS1, "1.5"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &OPTS1, "1.2345678901234567"); + write_float::<_, DECIMAL>(1.0e-17f64, &OPTS1, "1.0e-17"); + write_float::<_, DECIMAL>(9.99999999999999e-16f64, &OPTS1, "9.99999999999999e-16"); + write_float::<_, DECIMAL>(9.99999999999999e-15f64, &OPTS1, "9.99999999999999e-15"); + write_float::<_, DECIMAL>(0.00999999999999999f64, &OPTS1, "0.00999999999999999"); + write_float::<_, DECIMAL>(0.0999999999999999f64, &OPTS1, "0.0999999999999999"); + write_float::<_, DECIMAL>(0.999999999999999f64, &OPTS1, "0.999999999999999"); + write_float::<_, DECIMAL>(9.99999999999999f64, &OPTS1, "9.99999999999999"); + write_float::<_, DECIMAL>(99.9999999999999f64, &OPTS1, "99.9999999999999"); + write_float::<_, DECIMAL>(999.999999999999f64, &OPTS1, "999.999999999999"); + write_float::<_, DECIMAL>(1000.0f64, &OPTS1, "1000.0"); + write_float::<_, DECIMAL>(1.7976931348623157e308f64, &OPTS1, "1.7976931348623157e308"); + write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &OPTS1, "2.2250738585072014e-308"); + + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(50)).build_strict(); write_float::<_, DECIMAL>( 0.0f64, - &options, + &OPTS2, "0.0000000000000000000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1.0f64, - &options, + &OPTS2, "1.0000000000000000000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1.5f64, - &options, + &OPTS2, "1.5000000000000000000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1.2345678901234567890e0f64, - &options, + &OPTS2, "1.2345678901234567000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1.0e-17f64, - &options, + &OPTS2, "1.0000000000000000000000000000000000000000000000000e-17", ); write_float::<_, DECIMAL>( 9.99999999999999e-16f64, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000e-16", ); write_float::<_, DECIMAL>( 9.99999999999999e-15f64, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000e-15", ); write_float::<_, DECIMAL>( 0.00999999999999999f64, - &options, + &OPTS2, "0.0099999999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 0.0999999999999999f64, - &options, + &OPTS2, "0.099999999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 0.999999999999999f64, - &options, + &OPTS2, "0.99999999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 9.99999999999999f64, - &options, + &OPTS2, "9.9999999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 99.9999999999999f64, - &options, + &OPTS2, "99.999999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 999.999999999999f64, - &options, + &OPTS2, "999.99999999999900000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1000.0f64, - &options, + &OPTS2, "1000.0000000000000000000000000000000000000000000000", ); write_float::<_, DECIMAL>( 1.7976931348623157e308f64, - &options, + &OPTS2, "1.7976931348623157000000000000000000000000000000000e308", ); write_float::<_, DECIMAL>( 2.2250738585072014e-308f64, - &options, + &OPTS2, "2.2250738585072014000000000000000000000000000000000e-308", ); - let options = - Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0.0"); - write_float::<_, DECIMAL>(1.0f64, &options, "1.0"); - write_float::<_, DECIMAL>(1.5f64, &options, "1.5"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.2346"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1.0e-17"); - write_float::<_, DECIMAL>(9.99999999999999e-16f64, &options, "1.0e-15"); - write_float::<_, DECIMAL>(9.99999999999999e-15f64, &options, "1.0e-14"); - write_float::<_, DECIMAL>(0.00999999999999999f64, &options, "0.01"); - write_float::<_, DECIMAL>(0.0999999999999999f64, &options, "0.1"); - write_float::<_, DECIMAL>(0.999999999999999f64, &options, "1.0"); - write_float::<_, DECIMAL>(9.99999999999999f64, &options, "10.0"); - write_float::<_, DECIMAL>(99.9999999999999f64, &options, "100.0"); - write_float::<_, DECIMAL>(999.999999999999f64, &options, "1000.0"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000.0"); - write_float::<_, DECIMAL>(1.7976931348623157e308f64, &options, "1.7977e308"); - write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &options, "2.2251e-308"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0"); - write_float::<_, DECIMAL>(1.0f64, &options, "1"); - write_float::<_, DECIMAL>(1.5f64, &options, "1.5"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.2345678901234567"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1e-17"); - write_float::<_, DECIMAL>(9.99999999999999e-16f64, &options, "9.99999999999999e-16"); - write_float::<_, DECIMAL>(9.99999999999999e-15f64, &options, "9.99999999999999e-15"); - write_float::<_, DECIMAL>(0.00999999999999999f64, &options, "0.00999999999999999"); - write_float::<_, DECIMAL>(0.0999999999999999f64, &options, "0.0999999999999999"); - write_float::<_, DECIMAL>(0.999999999999999f64, &options, "0.999999999999999"); - write_float::<_, DECIMAL>(9.99999999999999f64, &options, "9.99999999999999"); - write_float::<_, DECIMAL>(99.9999999999999f64, &options, "99.9999999999999"); - write_float::<_, DECIMAL>(999.999999999999f64, &options, "999.999999999999"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000"); - write_float::<_, DECIMAL>(1.7976931348623157e308f64, &options, "1.7976931348623157e308"); - write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &options, "2.2250738585072014e-308"); - - let options = Options::builder() + const OPTS3: Options = + Options::builder().max_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float::<_, DECIMAL>(0.0f64, &OPTS3, "0.0"); + write_float::<_, DECIMAL>(1.0f64, &OPTS3, "1.0"); + write_float::<_, DECIMAL>(1.5f64, &OPTS3, "1.5"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &OPTS3, "1.2346"); + write_float::<_, DECIMAL>(1.0e-17f64, &OPTS3, "1.0e-17"); + write_float::<_, DECIMAL>(9.99999999999999e-16f64, &OPTS3, "1.0e-15"); + write_float::<_, DECIMAL>(9.99999999999999e-15f64, &OPTS3, "1.0e-14"); + write_float::<_, DECIMAL>(0.00999999999999999f64, &OPTS3, "0.01"); + write_float::<_, DECIMAL>(0.0999999999999999f64, &OPTS3, "0.1"); + write_float::<_, DECIMAL>(0.999999999999999f64, &OPTS3, "1.0"); + write_float::<_, DECIMAL>(9.99999999999999f64, &OPTS3, "10.0"); + write_float::<_, DECIMAL>(99.9999999999999f64, &OPTS3, "100.0"); + write_float::<_, DECIMAL>(999.999999999999f64, &OPTS3, "1000.0"); + write_float::<_, DECIMAL>(1000.0f64, &OPTS3, "1000.0"); + write_float::<_, DECIMAL>(1.7976931348623157e308f64, &OPTS3, "1.7977e308"); + write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &OPTS3, "2.2251e-308"); + + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float::<_, DECIMAL>(0.0f64, &OPTS4, "0"); + write_float::<_, DECIMAL>(1.0f64, &OPTS4, "1"); + write_float::<_, DECIMAL>(1.5f64, &OPTS4, "1.5"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &OPTS4, "1.2345678901234567"); + write_float::<_, DECIMAL>(1.0e-17f64, &OPTS4, "1e-17"); + write_float::<_, DECIMAL>(9.99999999999999e-16f64, &OPTS4, "9.99999999999999e-16"); + write_float::<_, DECIMAL>(9.99999999999999e-15f64, &OPTS4, "9.99999999999999e-15"); + write_float::<_, DECIMAL>(0.00999999999999999f64, &OPTS4, "0.00999999999999999"); + write_float::<_, DECIMAL>(0.0999999999999999f64, &OPTS4, "0.0999999999999999"); + write_float::<_, DECIMAL>(0.999999999999999f64, &OPTS4, "0.999999999999999"); + write_float::<_, DECIMAL>(9.99999999999999f64, &OPTS4, "9.99999999999999"); + write_float::<_, DECIMAL>(99.9999999999999f64, &OPTS4, "99.9999999999999"); + write_float::<_, DECIMAL>(999.999999999999f64, &OPTS4, "999.999999999999"); + write_float::<_, DECIMAL>(1000.0f64, &OPTS4, "1000"); + write_float::<_, DECIMAL>(1.7976931348623157e308f64, &OPTS4, "1.7976931348623157e308"); + write_float::<_, DECIMAL>(2.2250738585072014e-308f64, &OPTS4, "2.2250738585072014e-308"); + + const OPTS5: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(50)) .trim_floats(true) - .build() - .unwrap(); - write_float::<_, DECIMAL>(1.0e17f64, &options, "1e17"); - write_float::<_, DECIMAL>(1.0e-17f64, &options, "1e-17"); - write_float::<_, DECIMAL>(1000.0f64, &options, "1000"); + .build_strict(); + write_float::<_, DECIMAL>(1.0e17f64, &OPTS5, "1e17"); + write_float::<_, DECIMAL>(1.0e-17f64, &OPTS5, "1e-17"); + write_float::<_, DECIMAL>(1000.0f64, &OPTS5, "1000"); write_float::<_, DECIMAL>( 9.99999999999999e16f64, - &options, + &OPTS5, "9.9999999999999900000000000000000000000000000000000e16", ); write_float::<_, DECIMAL>( 9.99999999999999e-16f64, - &options, + &OPTS5, "9.9999999999999900000000000000000000000000000000000e-16", ); - let truncate = Options::builder() + const TRUNCATE: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - let round = Options::builder() + .build_strict(); + const ROUND: Options = Options::builder() .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Round) - .build() - .unwrap(); - - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &truncate, "1.234"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &round, "1.235"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &truncate, "12.34"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &round, "12.35"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &truncate, "123.4"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &round, "123.5"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &truncate, "1234.0"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &round, "1235.0"); + .build_strict(); + + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &TRUNCATE, "1.234"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &ROUND, "1.235"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &TRUNCATE, "12.34"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &ROUND, "12.35"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &TRUNCATE, "123.4"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &ROUND, "123.5"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &TRUNCATE, "1234.0"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &ROUND, "1235.0"); // Check min and max digits - let options = Options::builder() + const OPTS6: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(3)) .max_significant_digits(num::NonZeroUsize::new(4)) .round_mode(RoundMode::Truncate) - .build() - .unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0.00"); - write_float::<_, DECIMAL>(1.5f64, &options, "1.50"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.234"); + .build_strict(); + write_float::<_, DECIMAL>(0.0f64, &OPTS6, "0.00"); + write_float::<_, DECIMAL>(1.5f64, &OPTS6, "1.50"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &OPTS6, "1.234"); } // Test data for roundtrips. @@ -524,23 +520,23 @@ const F64_DATA: [f64; 33] = [ #[test] fn f32_test() { - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float::<_, DECIMAL>(0.0f32, &options, "0"); - write_float::<_, DECIMAL>(1.0f32, &options, "1"); - write_float::<_, DECIMAL>(10.0f32, &options, "10"); - write_float::<_, DECIMAL>(10.0f32, &options, "10"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f32, &options, "1.2345679"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f32, &options, "12.345679"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f32, &options, "123.45679"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f32, &options, "1234.5679"); + const OPTIONS: Options = Options::builder().trim_floats(true).build_strict(); + write_float::<_, DECIMAL>(0.0f32, &OPTIONS, "0"); + write_float::<_, DECIMAL>(1.0f32, &OPTIONS, "1"); + write_float::<_, DECIMAL>(10.0f32, &OPTIONS, "10"); + write_float::<_, DECIMAL>(10.0f32, &OPTIONS, "10"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f32, &OPTIONS, "1.2345679"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f32, &OPTIONS, "12.345679"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f32, &OPTIONS, "123.45679"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f32, &OPTIONS, "1234.5679"); } #[test] fn f32_roundtrip_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); for &float in F32_DATA.iter() { - let count = compact::write_float::<_, DECIMAL>(float, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(float, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); assert_eq!(roundtrip, Ok(float)); @@ -564,21 +560,21 @@ macro_rules! mut_b { #[test] fn write_float_scientific_test() { - let options = Options::new(); - write_float_scientific(mut_b!(b"1"), 0, &options, "1.0e0"); - write_float_scientific(mut_b!(b"999999999999999"), -1, &options, "9.99999999999999e-1"); - write_float_scientific(mut_b!(b"999999999999999"), 0, &options, "9.99999999999999e0"); - write_float_scientific(mut_b!(b"999999999999999"), -2, &options, "9.99999999999999e-2"); - write_float_scientific(mut_b!(b"17976931348623157"), 308, &options, "1.7976931348623157e308"); - write_float_scientific(mut_b!(b"22250738585072014"), -308, &options, "2.2250738585072014e-308"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_scientific(mut_b!(b"1"), 0, &options, "1e0"); - write_float_scientific(mut_b!(b"999999999999999"), -1, &options, "9.99999999999999e-1"); - write_float_scientific(mut_b!(b"999999999999999"), 0, &options, "9.99999999999999e0"); - write_float_scientific(mut_b!(b"999999999999999"), -2, &options, "9.99999999999999e-2"); - write_float_scientific(mut_b!(b"17976931348623157"), 308, &options, "1.7976931348623157e308"); - write_float_scientific(mut_b!(b"22250738585072014"), -308, &options, "2.2250738585072014e-308"); + const OPTS1: Options = Options::new(); + write_float_scientific(mut_b!(b"1"), 0, &OPTS1, "1.0e0"); + write_float_scientific(mut_b!(b"999999999999999"), -1, &OPTS1, "9.99999999999999e-1"); + write_float_scientific(mut_b!(b"999999999999999"), 0, &OPTS1, "9.99999999999999e0"); + write_float_scientific(mut_b!(b"999999999999999"), -2, &OPTS1, "9.99999999999999e-2"); + write_float_scientific(mut_b!(b"17976931348623157"), 308, &OPTS1, "1.7976931348623157e308"); + write_float_scientific(mut_b!(b"22250738585072014"), -308, &OPTS1, "2.2250738585072014e-308"); + + const OPTS2: Options = Options::builder().trim_floats(true).build_strict(); + write_float_scientific(mut_b!(b"1"), 0, &OPTS2, "1e0"); + write_float_scientific(mut_b!(b"999999999999999"), -1, &OPTS2, "9.99999999999999e-1"); + write_float_scientific(mut_b!(b"999999999999999"), 0, &OPTS2, "9.99999999999999e0"); + write_float_scientific(mut_b!(b"999999999999999"), -2, &OPTS2, "9.99999999999999e-2"); + write_float_scientific(mut_b!(b"17976931348623157"), 308, &OPTS2, "1.7976931348623157e308"); + write_float_scientific(mut_b!(b"22250738585072014"), -308, &OPTS2, "2.2250738585072014e-308"); } fn write_float_positive_exponent(digits: &mut [u8], k: i32, options: &Options, expected: &str) { @@ -597,19 +593,19 @@ fn write_float_positive_exponent(digits: &mut [u8], k: i32, options: &Options, e #[test] fn write_float_positive_exponent_test() { - let options = Options::new(); - write_float_positive_exponent(&mut [b'1'], 0, &options, "1.0"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 0, &options, "9.99999999999999"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 1, &options, "99.9999999999999"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 2, &options, "999.999999999999"); - write_float_positive_exponent(mut_b!(b"17976931348623157"), 308, &options, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_positive_exponent(&mut [b'1'], 0, &options, "1"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 0, &options, "9.99999999999999"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 1, &options, "99.9999999999999"); - write_float_positive_exponent(mut_b!(b"999999999999999"), 2, &options, "999.999999999999"); - write_float_positive_exponent(mut_b!(b"17976931348623157"), 308, &options, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + const OPTS1: Options = Options::new(); + write_float_positive_exponent(&mut [b'1'], 0, &OPTS1, "1.0"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 0, &OPTS1, "9.99999999999999"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 1, &OPTS1, "99.9999999999999"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 2, &OPTS1, "999.999999999999"); + write_float_positive_exponent(mut_b!(b"17976931348623157"), 308, &OPTS1, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0"); + + const OPTS2: Options = Options::builder().trim_floats(true).build_strict(); + write_float_positive_exponent(&mut [b'1'], 0, &OPTS2, "1"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 0, &OPTS2, "9.99999999999999"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 1, &OPTS2, "99.9999999999999"); + write_float_positive_exponent(mut_b!(b"999999999999999"), 2, &OPTS2, "999.999999999999"); + write_float_positive_exponent(mut_b!(b"17976931348623157"), 308, &OPTS2, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } fn write_float_negative_exponent(digits: &mut [u8], k: i32, options: &Options, expected: &str) { @@ -628,40 +624,40 @@ fn write_float_negative_exponent(digits: &mut [u8], k: i32, options: &Options, e #[test] fn write_float_negative_exponent_test() { - let options = Options::new(); - write_float_negative_exponent(&mut [b'1'], -1, &options, "0.1"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -3, &options, "0.00999999999999999"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -2, &options, "0.0999999999999999"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -1, &options, "0.999999999999999"); - write_float_negative_exponent(mut_b!(b"22250738585072014"), -308, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); - - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_negative_exponent(&mut [b'1'], -1, &options, "0.1"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -3, &options, "0.00999999999999999"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -2, &options, "0.0999999999999999"); - write_float_negative_exponent(mut_b!(b"999999999999999"), -1, &options, "0.999999999999999"); - write_float_negative_exponent(mut_b!(b"22250738585072014"), -308, &options, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); + const OPTS1: Options = Options::new(); + write_float_negative_exponent(&mut [b'1'], -1, &OPTS1, "0.1"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -3, &OPTS1, "0.00999999999999999"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -2, &OPTS1, "0.0999999999999999"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -1, &OPTS1, "0.999999999999999"); + write_float_negative_exponent(mut_b!(b"22250738585072014"), -308, &OPTS1, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); + + const OPTS2: Options = Options::builder().trim_floats(true).build_strict(); + write_float_negative_exponent(&mut [b'1'], -1, &OPTS2, "0.1"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -3, &OPTS2, "0.00999999999999999"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -2, &OPTS2, "0.0999999999999999"); + write_float_negative_exponent(mut_b!(b"999999999999999"), -1, &OPTS2, "0.999999999999999"); + write_float_negative_exponent(mut_b!(b"22250738585072014"), -308, &OPTS2, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014"); } #[test] fn f64_test() { - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float::<_, DECIMAL>(0.0f64, &options, "0"); - write_float::<_, DECIMAL>(1.0f64, &options, "1"); - write_float::<_, DECIMAL>(10.0f64, &options, "10"); - write_float::<_, DECIMAL>(10.0f64, &options, "10"); - write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &options, "1.2345678901234567"); - write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &options, "12.345678901234567"); - write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &options, "123.45678901234568"); - write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &options, "1234.567890123457"); + const OPTIONS: Options = Options::builder().trim_floats(true).build_strict(); + write_float::<_, DECIMAL>(0.0f64, &OPTIONS, "0"); + write_float::<_, DECIMAL>(1.0f64, &OPTIONS, "1"); + write_float::<_, DECIMAL>(10.0f64, &OPTIONS, "10"); + write_float::<_, DECIMAL>(10.0f64, &OPTIONS, "10"); + write_float::<_, DECIMAL>(1.2345678901234567890e0f64, &OPTIONS, "1.2345678901234567"); + write_float::<_, DECIMAL>(1.2345678901234567890e1f64, &OPTIONS, "12.345678901234567"); + write_float::<_, DECIMAL>(1.2345678901234567890e2f64, &OPTIONS, "123.45678901234568"); + write_float::<_, DECIMAL>(1.2345678901234567890e3f64, &OPTIONS, "1234.567890123457"); } #[test] fn f64_roundtrip_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); for &float in F64_DATA.iter() { - let count = compact::write_float::<_, DECIMAL>(float, &mut buffer, &options); + let count = compact::write_float::<_, DECIMAL>(float, &mut buffer, &OPTIONS); let actual = unsafe { std::str::from_utf8_unchecked(&buffer[..count]) }; let roundtrip = actual.parse::(); assert_eq!(roundtrip, Ok(float)); diff --git a/lexical-write-float/tests/hex_tests.rs b/lexical-write-float/tests/hex_tests.rs index 042db6b0..f7b68bd0 100644 --- a/lexical-write-float/tests/hex_tests.rs +++ b/lexical-write-float/tests/hex_tests.rs @@ -12,27 +12,27 @@ const BASE4_2_10: u128 = NumberFormatBuilder::new() .mantissa_radix(4) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const BASE8_2_10: u128 = NumberFormatBuilder::new() .mantissa_radix(8) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const BASE16_2_10: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const BASE32_2_10: u128 = NumberFormatBuilder::new() .mantissa_radix(32) .exponent_base(num::NonZeroU8::new(2)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const BASE16_4_10: u128 = NumberFormatBuilder::new() .mantissa_radix(16) .exponent_base(num::NonZeroU8::new(4)) .exponent_radix(num::NonZeroU8::new(10)) - .build(); + .build_strict(); const HEX_OPTIONS: Options = Options::builder().exponent(b'^').build_unchecked(); // NOTE: This doesn't handle float rounding or truncation. @@ -61,117 +61,117 @@ fn write_float_scientific_test() { // Positive exponent // Check no formatting, base4/2. - let options = Options::builder().build().unwrap(); - write_float_scientific::<_, BASE4_2_10>(0.0f64, &options, "0.0e0"); - write_float_scientific::<_, BASE4_2_10>(1.0f64, &options, "1.0e0"); - write_float_scientific::<_, BASE4_2_10>(2.0f64, &options, "2.0e0"); - write_float_scientific::<_, BASE4_2_10>(0.5f64, &options, "2.0e-2"); + const OPTS1: Options = Options::builder().build_strict(); + write_float_scientific::<_, BASE4_2_10>(0.0f64, &OPTS1, "0.0e0"); + write_float_scientific::<_, BASE4_2_10>(1.0f64, &OPTS1, "1.0e0"); + write_float_scientific::<_, BASE4_2_10>(2.0f64, &OPTS1, "2.0e0"); + write_float_scientific::<_, BASE4_2_10>(0.5f64, &OPTS1, "2.0e-2"); write_float_scientific::<_, BASE4_2_10>( 0.2345678901234567890e20f64, - &options, + &OPTS1, "1.10112013100111033030021213e64", ); write_float_scientific::<_, BASE4_2_10>( 0.1172839450617284e20f64, - &options, + &OPTS1, "2.20230032200222132120103032e62", ); write_float_scientific::<_, BASE4_2_10>( 0.0586419725308642e20f64, - &options, + &OPTS1, "1.10112013100111033030021213e62", ); write_float_scientific::<_, BASE4_2_10>( 0.0293209862654321e20f64, - &options, + &OPTS1, "2.20230032200222132120103032e60", ); write_float_scientific::<_, BASE4_2_10>( 0.01466049313271605e20f64, - &options, + &OPTS1, "1.10112013100111033030021213e60", ); write_float_scientific::<_, BASE4_2_10>( 0.2345678901234567890e-20f64, - &options, + &OPTS1, "2.30103300013110301132322302e-70", ); write_float_scientific::<_, BASE4_2_10>( 0.1172839450617284e-20f64, - &options, + &OPTS1, "1.12021320003222120233131121e-70", ); write_float_scientific::<_, BASE4_2_10>( 0.0586419725308642e-20f64, - &options, + &OPTS1, "2.30103300013110301132322302e-72", ); write_float_scientific::<_, BASE4_2_10>( 0.0293209862654321e-20f64, - &options, + &OPTS1, "1.12021320003222120233131121e-72", ); write_float_scientific::<_, BASE4_2_10>( 0.01466049313271605e-20f64, - &options, + &OPTS1, "2.30103300013110301132322302e-74", ); // Check no formatting, base8/2. - write_float_scientific::<_, BASE8_2_10>(0.0f64, &options, "0.0e0"); - write_float_scientific::<_, BASE8_2_10>(1.0f64, &options, "1.0e0"); - write_float_scientific::<_, BASE8_2_10>(2.0f64, &options, "2.0e0"); - write_float_scientific::<_, BASE8_2_10>(0.5f64, &options, "4.0e-3"); + write_float_scientific::<_, BASE8_2_10>(0.0f64, &OPTS1, "0.0e0"); + write_float_scientific::<_, BASE8_2_10>(1.0f64, &OPTS1, "1.0e0"); + write_float_scientific::<_, BASE8_2_10>(2.0f64, &OPTS1, "2.0e0"); + write_float_scientific::<_, BASE8_2_10>(0.5f64, &OPTS1, "4.0e-3"); write_float_scientific::<_, BASE8_2_10>( 0.2345678901234567890e20f64, - &options, + &OPTS1, "2.42607202517141147e63", ); write_float_scientific::<_, BASE8_2_10>( 0.1172839450617284e20f64, - &options, + &OPTS1, "1.213035012474604634e63", ); write_float_scientific::<_, BASE8_2_10>( 0.0586419725308642e20f64, - &options, + &OPTS1, "5.05416405236302316e60", ); write_float_scientific::<_, BASE8_2_10>( 0.0293209862654321e20f64, - &options, + &OPTS1, "2.42607202517141147e60", ); write_float_scientific::<_, BASE8_2_10>( 0.01466049313271605e20f64, - &options, + &OPTS1, "1.213035012474604634e60", ); write_float_scientific::<_, BASE8_2_10>( 0.2345678901234567890e-20f64, - &options, + &OPTS1, "1.304740165142756544e-69", ); write_float_scientific::<_, BASE8_2_10>( 0.1172839450617284e-20f64, - &options, + &OPTS1, "5.42360072461367262e-72", ); write_float_scientific::<_, BASE8_2_10>( 0.0586419725308642e-20f64, - &options, + &OPTS1, "2.61170035230573531e-72", ); write_float_scientific::<_, BASE8_2_10>( 0.0293209862654321e-20f64, - &options, + &OPTS1, "1.304740165142756544e-72", ); write_float_scientific::<_, BASE8_2_10>( 0.01466049313271605e-20f64, - &options, + &OPTS1, "5.42360072461367262e-75", ); @@ -347,38 +347,37 @@ fn write_float_scientific_test() { ); // Check with a minimum number of digits. - let options = - Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build().unwrap(); - write_float_scientific::<_, BASE16_4_10>(0.0f64, &options, "0.0000e0"); - write_float_scientific::<_, BASE16_4_10>(1.0f64, &options, "1.0000e0"); - write_float_scientific::<_, BASE16_4_10>(2.0f64, &options, "2.0000e0"); - write_float_scientific::<_, BASE16_4_10>(0.5f64, &options, "8.0000e-2"); + const OPTS2: Options = + Options::builder().min_significant_digits(num::NonZeroUsize::new(5)).build_strict(); + write_float_scientific::<_, BASE16_4_10>(0.0f64, &OPTS2, "0.0000e0"); + write_float_scientific::<_, BASE16_4_10>(1.0f64, &OPTS2, "1.0000e0"); + write_float_scientific::<_, BASE16_4_10>(2.0f64, &OPTS2, "2.0000e0"); + write_float_scientific::<_, BASE16_4_10>(0.5f64, &OPTS2, "8.0000e-2"); write_float_scientific::<_, BASE16_4_10>( 0.2345678901234567890e2f64, - &options, + &OPTS2, "1.774F01FED3264e2", ); - let options = Options::builder() + const OPTS3: Options = Options::builder() .min_significant_digits(num::NonZeroUsize::new(5)) .trim_floats(true) - .build() - .unwrap(); - write_float_scientific::<_, BASE16_4_10>(0.0f64, &options, "0e0"); - write_float_scientific::<_, BASE16_4_10>(1.0f64, &options, "1e0"); - write_float_scientific::<_, BASE16_4_10>(2.0f64, &options, "2e0"); - write_float_scientific::<_, BASE16_4_10>(0.5f64, &options, "8e-2"); + .build_strict(); + write_float_scientific::<_, BASE16_4_10>(0.0f64, &OPTS3, "0e0"); + write_float_scientific::<_, BASE16_4_10>(1.0f64, &OPTS3, "1e0"); + write_float_scientific::<_, BASE16_4_10>(2.0f64, &OPTS3, "2e0"); + write_float_scientific::<_, BASE16_4_10>(0.5f64, &OPTS3, "8e-2"); write_float_scientific::<_, BASE16_4_10>( 0.2345678901234567890e2f64, - &options, + &OPTS3, "1.774F01FED3264e2", ); // Check trimming floats - let options = Options::builder().trim_floats(true).build().unwrap(); - write_float_scientific::<_, BASE16_4_10>(1f32, &options, "1e0"); - write_float_scientific::<_, BASE16_4_10>(1.4e-45f32, &options, "8e-76"); - write_float_scientific::<_, BASE16_4_10>(1.2345678901234567890f32, &options, "1.3C0CA4e0"); + const OPTS4: Options = Options::builder().trim_floats(true).build_strict(); + write_float_scientific::<_, BASE16_4_10>(1f32, &OPTS4, "1e0"); + write_float_scientific::<_, BASE16_4_10>(1.4e-45f32, &OPTS4, "8e-76"); + write_float_scientific::<_, BASE16_4_10>(1.2345678901234567890f32, &OPTS4, "1.3C0CA4e0"); } // NOTE: This doesn't handle float rounding or truncation. @@ -395,19 +394,19 @@ where #[test] fn write_float_test() { - let options = Options::builder().build().unwrap(); + const OPTIONS: Options = Options::builder().build_strict(); write_float::<_, BASE4_2_10>( 0.2345678901234567890f64, - &options, + &OPTIONS, "0.033000302210022030112133232", ); - write_float::<_, BASE4_2_10>(0.1172839450617284f64, &options, "0.013200121102011012023033313"); - write_float::<_, BASE4_2_10>(0.0586419725308642f64, &options, "0.0033000302210022030112133232"); - write_float::<_, BASE4_2_10>(0.0293209862654321f64, &options, "1.3200121102011012023033313e-6"); + write_float::<_, BASE4_2_10>(0.1172839450617284f64, &OPTIONS, "0.013200121102011012023033313"); + write_float::<_, BASE4_2_10>(0.0586419725308642f64, &OPTIONS, "0.0033000302210022030112133232"); + write_float::<_, BASE4_2_10>(0.0293209862654321f64, &OPTIONS, "1.3200121102011012023033313e-6"); write_float::<_, BASE4_2_10>( 0.01466049313271605f64, - &options, + &OPTIONS, "3.3000302210022030112133232e-8", ); } diff --git a/lexical-write-float/tests/options_tests.rs b/lexical-write-float/tests/options_tests.rs index 68805713..cb2da54d 100644 --- a/lexical-write-float/tests/options_tests.rs +++ b/lexical-write-float/tests/options_tests.rs @@ -4,7 +4,7 @@ use lexical_write_float::options::{self, Options, OptionsBuilder}; #[test] fn invalid_exponent_test() { - let mut builder = OptionsBuilder::default(); + let mut builder = OptionsBuilder::new(); builder = builder.exponent(b'\x00'); assert!(!builder.is_valid()); builder = builder.exponent(b'\x7f'); @@ -17,7 +17,7 @@ fn invalid_exponent_test() { #[test] fn invalid_decimal_point_test() { - let mut builder = OptionsBuilder::default(); + let mut builder = OptionsBuilder::new(); builder = builder.decimal_point(b'\x00'); assert!(!builder.is_valid()); builder = builder.decimal_point(b'\x7f'); @@ -30,7 +30,7 @@ fn invalid_decimal_point_test() { #[test] fn invalid_nan_test() { - let mut builder = OptionsBuilder::default(); + let mut builder = OptionsBuilder::new(); builder = builder.nan_string(Some(b"naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan")); assert!(!builder.is_valid()); builder = builder.nan_string(Some(b"inf")); @@ -47,7 +47,7 @@ fn invalid_nan_test() { #[test] fn invalid_inf_test() { - let mut builder = OptionsBuilder::default(); + let mut builder = OptionsBuilder::new(); builder = builder.inf_string(Some(b"innnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnf")); assert!(!builder.is_valid()); builder = builder.inf_string(Some(b"nan")); @@ -64,7 +64,7 @@ fn invalid_inf_test() { #[test] fn builder_test() { - let mut builder = OptionsBuilder::default(); + let mut builder = OptionsBuilder::new(); builder = builder.max_significant_digits(num::NonZeroUsize::new(10)); builder = builder.min_significant_digits(num::NonZeroUsize::new(5)); @@ -93,6 +93,7 @@ fn builder_test() { } #[test] +#[allow(deprecated)] fn options_test() { let mut opts = Options::new(); diff --git a/lexical-write-integer/Cargo.toml b/lexical-write-integer/Cargo.toml index 9c4302ab..f195008b 100644 --- a/lexical-write-integer/Cargo.toml +++ b/lexical-write-integer/Cargo.toml @@ -18,9 +18,6 @@ exclude = [ "cargo-timing*.html" ] -[dependencies] -static_assertions = "1.1.0" - [dependencies.lexical-util] version = "1.0.5" path = "../lexical-util" @@ -48,3 +45,4 @@ lint = ["lexical-util/lint"] [package.metadata.docs.rs] features = ["radix", "format"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical-write-integer/src/algorithm.rs b/lexical-write-integer/src/algorithm.rs index 1ca0c755..1d508c37 100644 --- a/lexical-write-integer/src/algorithm.rs +++ b/lexical-write-integer/src/algorithm.rs @@ -1,18 +1,17 @@ //! Radix-generic, optimized, integer-to-string conversion routines. //! //! These routines are highly optimized: they unroll 4 loops at a time, -//! using pre-computed base^2 tables. +//! using pre-computed base^2 tables. This was popularized by Andrei +//! Alexandrescu, and uses 2 digits per division, which we further optimize in +//! up to 4 digits per division with a bit shift. //! -//! This was popularized by Andrei Alexandrescu, and uses 2 digits per -//! division, which we further optimize in up to 4 digits per division -//! with a bit shift. -//! -//! See [Algorithm.md](/docs/Algorithm.md) for a more detailed description of -//! the algorithm choice here. See [Benchmarks.md](/docs/Benchmarks.md) for +//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of +//! the algorithm choice here. See [Benchmarks](/docs/Benchmarks.md) for //! recent benchmark data. #![cfg(not(feature = "compact"))] #![cfg(feature = "power-of-two")] +#![doc(hidden)] use lexical_util::assert::debug_assert_radix; use lexical_util::digit::digit_to_char; @@ -24,13 +23,15 @@ use lexical_util::step::u64_step; use crate::digit_count::DigitCount; /// Index a buffer and get a mutable reference, without bounds checking. -/// The `($x:ident[$i:expr] = $y:ident[$j:expr])` is not used with `compact`. +/// The `($x:ident[$i:expr] = $y:ident[$j:expr])` is not used with [`compact`]. /// The newer version of the lint is `unused_macro_rules`, but this isn't /// supported until nightly-2022-05-12. /// /// By default, writers tend to be safe, due to Miri, Valgrind, /// and other tests and careful validation against a wide range /// of randomized input. Parsers are much trickier to validate. +/// +/// [`compact`]: crate#compact #[allow(unknown_lints, unused_macro_rules)] macro_rules! i { ($x:ident[$i:expr]) => { diff --git a/lexical-write-integer/src/api.rs b/lexical-write-integer/src/api.rs index fcbd93da..08f58534 100644 --- a/lexical-write-integer/src/api.rs +++ b/lexical-write-integer/src/api.rs @@ -101,8 +101,8 @@ macro_rules! unsigned_to_lexical { )*) } -to_lexical! {} -to_lexical_with_options! {} +to_lexical!("lexical_write_integer", 1234, u64); +to_lexical_with_options!("lexical_write_integer", 1234, u64, Options); unsigned_to_lexical! { u8 u16 u32 u64 u128 usize } // Implement `ToLexical` for numeric type. diff --git a/lexical-write-integer/src/compact.rs b/lexical-write-integer/src/compact.rs index 0217986f..8cda06a1 100644 --- a/lexical-write-integer/src/compact.rs +++ b/lexical-write-integer/src/compact.rs @@ -56,7 +56,7 @@ pub trait Compact: UnsignedInteger + FormattedSize { } // Decode last digit. - let r = value % radix; + let r = value; index -= 1; digits[index] = digit_to_char(u32::as_cast(r)); let slc = &digits[index..]; diff --git a/lexical-write-integer/src/jeaiii.rs b/lexical-write-integer/src/jeaiii.rs index bd041454..51683a2c 100644 --- a/lexical-write-integer/src/jeaiii.rs +++ b/lexical-write-integer/src/jeaiii.rs @@ -15,8 +15,8 @@ //! paradoxically seems to improve performance, potentially due to less //! branching. //! -//! See [Algorithm.md](/docs/Algorithm.md) for a more detailed description of -//! the algorithm choice here. See [Benchmarks.md](/docs/Benchmarks.md) for +//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of +//! the algorithm choice here. See [Benchmarks](/docs/Benchmarks.md) for //! recent benchmark data. //! //! [`Faster Integer Formatting`]: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ diff --git a/lexical-write-integer/src/lib.rs b/lexical-write-integer/src/lib.rs index e4128ceb..7cd7a1ad 100644 --- a/lexical-write-integer/src/lib.rs +++ b/lexical-write-integer/src/lib.rs @@ -1,66 +1,130 @@ //! Fast lexical integer-to-string conversion routines. //! -//! The default implementations use power reduction to unroll -//! 4 loops at a time to minimize the number of required divisions, -//! leading to massive performance gains. In addition, decimal -//! strings pre-calculate the number of digits, avoiding temporary buffers. +//! This contains high-performance methods to write integers +//! directly to bytes, can be converted to [`str`] using +//! [`str::from_utf8`]. Using [`to_lexical`] is analogous to [`to_string`], +//! just writing to an existing buffer. //! -//! A compact, fallback algorithm uses a naive, simple algorithm, -//! where each loop generates a single digit. This comes at a performance -//! penalty, but produces smaller binaries. +//! [`str::from_utf8`]: core::str::from_utf8 +//! [`to_lexical`]: ToLexical::to_lexical +//! +//! # Getting Started +//! +//! To write a number to bytes, use [`to_lexical`]: +//! +//! ```rust +//! # #[no_std] +//! # use core::str; +//! use lexical_write_integer::{FormattedSize, ToLexical}; +//! +//! let mut buffer = [0u8; u64::FORMATTED_SIZE_DECIMAL]; +//! let digits = 1234u64.to_lexical(&mut buffer); +//! assert_eq!(str::from_utf8(digits), Ok("1234")); +//! ``` +//! +//! Using [`FormattedSize::FORMATTED_SIZE_DECIMAL`] guarantees the buffer +//! will be large enough to write the digits for all numbers of that +//! type. //! //! # Features //! -//! * `std` - Use the standard library. +//! * `format` - Add support for custom integer formatting (currently +//! unsupported). //! * `power-of-two` - Add support for writing power-of-two integer strings. //! * `radix` - Add support for strings of any radix. //! * `compact` - Reduce code size at the cost of performance. -//! * `safe` - Ensure only memory-safe indexing is used. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. //! -//! # Note +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html //! -//! Only documented functionality is considered part of the public API: -//! any of the modules, internal functions, or structs may change -//! release-to-release without major or minor version changes. Use -//! internal implementation details at your own risk. +//! A complete description of supported features includes: //! -//! lexical-write-integer mainly exists as an implementation detail for -//! lexical-core, although its API is stable. If you would like to use -//! a high-level API that writes to and parses from `String` and `&str`, -//! respectively, please look at [lexical](https://crates.io/crates/lexical) -//! instead. If you would like an API that supports multiple numeric -//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core) -//! instead. +//! #### format //! -//! # Version Support +//! Add support for custom integer formatting. Currently no custom styles are +//! supported but this could include digit [`separator`] support in the future. //! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. +//! [`separator`]: NumberFormatBuilder::digit_separator //! -//! # Design +//! //! -//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) -//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md) -//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! #### power-of-two //! -//! # Safety +//! Enable writing numbers using radixes that are powers of two, that is, `2`, +//! `4`, `8`, `16`, and `32`. In these cases, you should use [`FORMATTED_SIZE`] +//! to create a sufficiently large buffer. //! -//! This module uses a some more unsafe code for moderately acceptable -//! performance. The compact decimal serializer has no non-local safety -//! invariants, which since it's focused on code size rather than performance, -//! this trade-off is acceptable and it uses a temporary, over-allocated buffer -//! as an intermediate. +//! [`FORMATTED_SIZE`]: FormattedSize::FORMATTED_SIZE //! -//! The decimal writer relies on pre-computed tables and an exact calculation -//! of the digit count ([`digit_count`]) to avoid any overhead. Avoid -//! intermediary copies is **CRITICAL** for fast performance so the entire -//! buffer must be known but assigned to use algorithms the compiler cannot -//! easily verify. This is because we use multi-digit optimizations with our -//! pre-computed tables, so we cannot just iterate over the slice and assign -//! iteratively. Using checked indexing can lead to 30%+ decreases in -//! performance. However, with careful analysis and factoring of the code, it's -//! fairly easy to demonstrate the safety as long as the caller ensures at least -//! the required number of digits are provided. +//! ```rust +//! # #[cfg(feature = "power-of-two")] { +//! # use core::str; +//! use lexical_write_integer::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions}; +//! +//! let mut buffer = [0u8; u64::FORMATTED_SIZE]; +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! const OPTIONS: Options = Options::new(); +//! let digits = 1234u64.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("10011010010")); +//! # } +//! ``` +//! +//! #### radix +//! +//! Enable writing numbers using all radixes from `2` to `36`. This requires +//! more static storage than [`power-of-two`][crate#power-of-two], and increases +//! compile times, but can be quite useful for esoteric programming languages +//! which use duodecimal integers, for example. +//! +//! ```rust +//! # #[cfg(feature = "radix")] { +//! # use core::str; +//! use lexical_write_integer::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions}; +//! +//! let mut buffer = [0u8; u64::FORMATTED_SIZE]; +//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12); +//! const OPTIONS: Options = Options::new(); +//! let digits = 1234u64.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("86A")); +//! # } +//! ``` +//! +//! #### compact +//! +//! Reduce the generated code size at the cost of performance. This minimizes +//! the number of static tables, inlining, and generics used, drastically +//! reducing the size of the generated binaries. +//! +//! #### std +//! +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. +//! +//! # Higher-Level APIs +//! +//! If you would like support for writing to [`String`] directly, use +//! [`lexical`] instead. If you would like an API that supports multiple numeric +//! conversions rather than just writing integers, use [`lexical-core`] instead. +//! +//! [`lexical`]: https://crates.io/crates/lexical +//! [`lexical-core`]: https://crates.io/crates/lexical-core +//! +//! # Version Support +//! +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! +//! # Algorithm +//! +//! We use 3 algorithms for serializing numbers: +//! 1. [`Jeaiii Algorithm`] (decimal only) +//! 2. Power reduction to write 4 digits at a time (radix only) +//! 3. Compact, single-digit serialization //! //! ## Decimal //! @@ -97,6 +161,9 @@ //! 3 2 6 7 4 5 //! ``` //! +//! For larger integers, we can apply the a similar algorithm with minor +//! modifications to minimize branching while keeping excellent performance. +//! //! ## Radix //! //! Our radix-based algorithms work like this, carving off the lower digits and @@ -108,6 +175,8 @@ //! let digits = value.digit_count(); //! let bytes = buffer[..digits]; //! +//! let table = ...; // some pre-computed table of 2 * radix^2 length +//! //! let radix = 10; //! let radix2 = radix * radix; //! let radix4 = radix2 * radix2 @@ -138,21 +207,122 @@ //! tables are large enough so there are no non-local safety considerations //! there. The current logic call stack is: //! 1. [`to_lexical`] -//! 2. [`decimal`][`dec`], compact, or radix (gets the correct tables and calls -//! algorithm) +//! 2. [`decimal`][`dec`], [`compact`][`cmp`], or [`radix`][`rdx`] (gets the +//! correct tables and calls algorithm) //! 3. [`jeaiii`] //! -//! [`digit_count`]: crate::digit_count::DigitCount +//! # Compact +//! +//! A compact, fallback algorithm uses a naive, simple algorithm, +//! where each loop generates a single digit. This comes at a performance +//! penalty, but produces smaller binaries. It is analogous to the below +//! code. +//! +//! ```rust,ignore +//! const fn digit_to_char(digit: u32) -> u8 { +//! match r { +//! b'0'..=b'9' => c - b'0', +//! b'A'..=b'Z' => c - b'A' + 10, +//! b'a'..=b'z' => c - b'a' + 10, +//! _ => 0xFF, // unreachable +//! } +//! } +//! +//! let mut value = 12345u32; +//! let buffer = [0u8; 32]; +//! let digits = value.digit_count(); +//! let bytes = buffer[..digits]; +//! +//! let radix = 10; +//! let mut index = bytes.len(); +//! while value >= radix { +//! let r = value % radix; +//! value /= radix; +//! index -= 1; +//! bytes[index] = digit_to_char(r); +//! } +//! +//! index -= 1; +//! bytes[index] = digit_to_char(value); +//! ``` +//! +//! # Design +//! +//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) +//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md) +//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! +//! # Safety Guarantees +//! +//!
+//! +//! +//! This module uses some unsafe code to achieve accept acceptable performance. +//! Providing a buffer of insufficient size will cause the code to panic and +//! cannot lead to out-of-bounds access. The safety guarantees and logic are +//! described below. +//! +//!
+//! +//! ### Decimal +//! +//! Our decimal writer uses a branched algorithm and therefore the indexing for +//! each element in the buffer is known ahead of time. The digit +//! [`generation`][`digit-gen`] is [well-established][`Jeaiii Algorithm`] to +//! ensure the the lookup value is less than the size of the pre-computed table +//! (`2 * 10^2`, or 200), and as long as this invariant holds true, then no +//! undefined behavior can occur. +//! +//! [`digit-gen`]: https://github.com/jk-jeon/idiv/blob/main/subproject/example/jeaiii_analysis.cpp +//! +//! ### Radix +//! +//! The non-decimal writers rely on pre-computed tables and an exact calculation +//! of the digit count ([`digit_count`]) to avoid any overhead. Avoiding +//! intermediary copies is **CRITICAL** for fast performance so the entire +//! buffer must be known but assigned to use algorithms the compiler cannot +//! easily verify. This is because we use multi-digit optimizations with our +//! pre-computed tables, so we cannot just iterate over the slice and assign +//! iteratively. Using checked indexing for the pre-compuited table can lead to +//! 30%+ decreases in performance. However, with careful analysis and factoring +//! of the code, it's trivial to demonstrate both the table lookups and buffer +//! indexing are safe. +//! +//! For radixes that are 2^N, we use the `ceil(log(value | 1, radix))` which can +//! always be calculated through the number of leading [`zeros`][`log2_lz`]. For +//! other radixes, we calculate the number of digits exactly the same way as if +//! we were writing digits in an initial pass. +//! +//! ### Compact +//! +//! The compact decimal writer uses no unsafe indexing. +//! +//! [`digit_count`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/digit_count.rs#L180 //! [`to_lexical`]: crate::ToLexical::to_lexical -//! [`dec`]: crate::decimal::Decimal::decimal -//! [`jeaiii`]: crate::jeaiii +//! [`dec`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/decimal.rs#L278 +//! [`jeaiii`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/jeaiii.rs +//! [`cmp`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/compact.rs +//! [`rdx`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/radix.rs //! [`Jeaiii Algorithm`]: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html +//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html +//! [`to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string +//! [`log2_lz`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-write-integer/src/digit_count.rs#L119 // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -195,8 +365,10 @@ mod table_radix; // Re-exports pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; -pub use lexical_util::format::{self, NumberFormatBuilder}; +pub use lexical_util::error::Error; +pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder}; pub use lexical_util::options::WriteOptions; +pub use lexical_util::result::Result; pub use self::api::{ToLexical, ToLexicalWithOptions}; #[doc(inline)] diff --git a/lexical-write-integer/src/options.rs b/lexical-write-integer/src/options.rs index fd5b3c5e..6e460330 100644 --- a/lexical-write-integer/src/options.rs +++ b/lexical-write-integer/src/options.rs @@ -1,13 +1,49 @@ //! Configuration options for writing integers. //! -//! This is a dummy implementation, since writing integers never have options. +//! This currently has no functionality, since we do not +//! support any features for writing integers at this time. +//! +//! # Examples +//! +//! ```rust +//! # use core::str; +//! use lexical_write_integer::{Options, ToLexicalWithOptions}; +//! use lexical_write_integer::format::STANDARD; +//! +//! const OPTIONS: Options = Options::builder() +//! .build_strict(); +//! +//! const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::(); +//! let mut buffer = [0u8; BUFFER_SIZE]; +//! let value = 1234u64; +//! let digits = value.to_lexical_with_options::(&mut buffer, &OPTIONS); +//! assert_eq!(str::from_utf8(digits), Ok("1234")); +//! ``` use lexical_util::constants::FormattedSize; +use lexical_util::format::NumberFormat; use lexical_util::options::WriteOptions; use lexical_util::result::Result; -use static_assertions::const_assert; -/// Builder for `Options`. +/// Builder for [`Options`]. +/// +/// # Examples +/// +/// ```rust +/// use core::str; +/// +/// use lexical_write_integer::{Options, ToLexicalWithOptions}; +/// use lexical_write_integer::format::STANDARD; +/// +/// const OPTIONS: Options = Options::builder() +/// .build_strict(); +/// +/// const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::(); +/// let mut buffer = [0u8; BUFFER_SIZE]; +/// let value = 1234u64; +/// let digits = value.to_lexical_with_options::(&mut buffer, &OPTIONS); +/// assert_eq!(str::from_utf8(digits), Ok("1234")); +/// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OptionsBuilder {} @@ -26,13 +62,32 @@ impl OptionsBuilder { true } - /// Build the `Options` struct with bounds validation. + /// Build the [`Options`] struct without validation. + /// + ///
+ /// + /// This is completely safe, however, misusing this could cause panics at + /// runtime. Always check if [`is_valid`] prior to using the built + /// options. + /// + ///
+ /// + /// [`is_valid`]: Self::is_valid #[inline(always)] pub const fn build_unchecked(&self) -> Options { Options {} } - /// Build the `Options` struct. + /// Build the [`Options`] struct. This can never panic. + #[inline(always)] + pub const fn build_strict(&self) -> Options { + match self.build() { + Ok(value) => value, + Err(error) => core::panic!("{}", error.description()), + } + } + + /// Build the [`Options`] struct. #[inline(always)] pub const fn build(&self) -> Result { Ok(self.build_unchecked()) @@ -51,14 +106,22 @@ impl Default for OptionsBuilder { /// # Examples /// /// ```rust -/// use lexical_write_integer::options::Options; +/// use core::str; /// -/// # pub fn main() { -/// let options = Options::builder() -/// .build() -/// .unwrap(); -/// # } +/// use lexical_write_integer::{Options, ToLexicalWithOptions}; +/// use lexical_write_integer::format::STANDARD; +/// +/// const OPTIONS: Options = Options::builder() +/// .build_strict(); +/// +/// const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::(); +/// let mut buffer = [0u8; BUFFER_SIZE]; +/// let value = 1234u64; +/// let digits = value.to_lexical_with_options::(&mut buffer, &OPTIONS); +/// assert_eq!(str::from_utf8(digits), Ok("1234")); /// ``` +// FIXME: Add phantom data for private fields. +// This is a BREAKING change so requires a major API release. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Options {} @@ -75,15 +138,29 @@ impl Options { true } + /// Get an upper bound on the required buffer size. + /// + /// This is always [`FORMATTED_SIZE`][FormattedSize::FORMATTED_SIZE] + /// or [`FORMATTED_SIZE_DECIMAL`][FormattedSize::FORMATTED_SIZE_DECIMAL], + /// depending on the radix. + #[inline(always)] + pub const fn buffer_size_const(&self) -> usize { + if (NumberFormat:: {}.radix()) == 10 { + T::FORMATTED_SIZE_DECIMAL + } else { + T::FORMATTED_SIZE + } + } + // BUILDERS - /// Get `OptionsBuilder` as a static function. + /// Get [`OptionsBuilder`] as a static function. #[inline(always)] pub const fn builder() -> OptionsBuilder { OptionsBuilder::new() } - /// Create `OptionsBuilder` using existing values. + /// Create [`OptionsBuilder`] using existing values. #[inline(always)] pub const fn rebuild(&self) -> OptionsBuilder { OptionsBuilder {} @@ -103,9 +180,10 @@ impl WriteOptions for Options { Self::is_valid(self) } + #[doc = lexical_util::write_options_doc!()] #[inline(always)] fn buffer_size(&self) -> usize { - T::FORMATTED_SIZE + self.buffer_size_const::() } } @@ -115,4 +193,3 @@ impl WriteOptions for Options { /// Standard number format. #[rustfmt::skip] pub const STANDARD: Options = Options::new(); -const_assert!(STANDARD.is_valid()); diff --git a/lexical-write-integer/src/radix.rs b/lexical-write-integer/src/radix.rs index ff6fc6a2..02b8fc9a 100644 --- a/lexical-write-integer/src/radix.rs +++ b/lexical-write-integer/src/radix.rs @@ -5,7 +5,7 @@ //! reasons, it makes no sense to pre-compute the number of digits, //! and therefore //! -//! See [Algorithm.md](/docs/Algorithm.md) for a more detailed description of +//! See [Algorithm](/docs/Algorithm.md) for a more detailed description of //! the algorithm choice here. #![cfg(not(feature = "compact"))] diff --git a/lexical-write-integer/tests/api_tests.rs b/lexical-write-integer/tests/api_tests.rs index df4e4d6a..c2239be0 100644 --- a/lexical-write-integer/tests/api_tests.rs +++ b/lexical-write-integer/tests/api_tests.rs @@ -33,11 +33,11 @@ roundtrip_impl! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } #[cfg(feature = "format")] fn mandatory_sign_test() { let mut buffer = [b'\x00'; 16]; - let options = Options::new(); - const FORMAT: u128 = NumberFormatBuilder::new().required_mantissa_sign(true).build(); - assert_eq!(b"+0", 0i8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &options)); - assert_eq!(b"-1", (-1i8).to_lexical_with_options::<{ FORMAT }>(&mut buffer, &options)); - assert_eq!(b"+1", 1i8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &options)); + const OPTIONS: Options = Options::new(); + const FORMAT: u128 = NumberFormatBuilder::new().required_mantissa_sign(true).build_strict(); + assert_eq!(b"+0", 0i8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &OPTIONS)); + assert_eq!(b"-1", (-1i8).to_lexical_with_options::<{ FORMAT }>(&mut buffer, &OPTIONS)); + assert_eq!(b"+1", 1i8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &OPTIONS)); } #[test] @@ -186,24 +186,24 @@ fn i128_test() { fn proptest_failures_radix() { const FORMAT: u128 = from_radix(12); let mut buffer = [b'\x00'; BUFFER_SIZE]; - let options = Options::new(); - assert_eq!(b"A8", 128u8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &options)); + const OPTIONS: Options = Options::new(); + assert_eq!(b"A8", 128u8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &OPTIONS)); } #[test] fn options_test() { let mut buffer = [b'\x00'; 48]; - let options = Options::new(); - assert_eq!(b"0", 0u8.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0u16.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0u32.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0u64.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0u128.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0i8.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0i16.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0i32.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0i64.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); - assert_eq!(b"0", 0i128.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &options)); + const OPTIONS: Options = Options::new(); + assert_eq!(b"0", 0u8.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0u16.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0u32.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0u64.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0u128.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0i8.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0i16.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0i32.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0i64.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); + assert_eq!(b"0", 0i128.to_lexical_with_options::<{ STANDARD }>(&mut buffer, &OPTIONS)); } #[test] @@ -211,8 +211,8 @@ fn options_test() { fn options_radix_test() { const FORMAT: u128 = from_radix(12); let mut buffer = [b'\x00'; 128]; - let options = Options::new(); - assert_eq!(b"A8", 128u8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &options)); + const OPTIONS: Options = Options::new(); + assert_eq!(b"A8", 128u8.to_lexical_with_options::<{ FORMAT }>(&mut buffer, &OPTIONS)); } fn roundtrip(x: T) -> T diff --git a/lexical/Cargo.toml b/lexical/Cargo.toml index e6759a37..aa4ebcb3 100644 --- a/lexical/Cargo.toml +++ b/lexical/Cargo.toml @@ -10,7 +10,7 @@ name = "lexical" readme = "README.md" repository = "https://github.com/Alexhuszagh/rust-lexical" version = "7.0.4" -rust-version = "1.61.0" +rust-version = "1.60.0" exclude = [ "assets/*", "docs/*", @@ -74,3 +74,4 @@ f128 = ["lexical-core/f128"] [package.metadata.docs.rs] features = ["radix", "format", "write-integers", "write-floats", "parse-integers", "parse-floats", "f16"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/lexical/src/lib.rs b/lexical/src/lib.rs index 722046d7..10cd065a 100644 --- a/lexical/src/lib.rs +++ b/lexical/src/lib.rs @@ -1,52 +1,174 @@ //! Fast lexical conversion routines. //! -//! Fast lexical conversion routines for both `std` and `no_std` environments. -//! lexical provides routines to convert numbers to and from decimal -//! strings. lexical also supports non-base 10 numbers, with the `radix` -//! feature, for both integers and floats. lexical is customizable -//! and yet simple to use: despite supporting nearly every float and -//! integer format available, it only exports 2 write functions -//! and 4 parse functions. -//! -//! lexical is well-tested, and has been downloaded more than 5 million -//! times and currently has no known errors in correctness. lexical -//! prioritizes performance above all else, and aims to be competitive -//! or faster than any other float or integer parser and writer. +//! `lexical-core` is a high-performance library for number-to-string and +//! string-to-number conversions. The writers require a system allocator, +//! but support a [`no_std`] environment. In addition to high performance, +//! it's also highly configurable, supporting nearly every float and integer +//! format available. +//! +//! `lexical` is well-tested, and has been downloaded more than 25 million +//! times and currently has no known errors in correctness. `lexical` +//! prioritizes performance above all else, and is competitive or faster +//! than any other float or integer parser and writer. +//! +//! In addition, despite having a large number of features, configurability, +//! and a focus on performance, it also aims to have fast compile times. +//! Recent versions also add [`support`](#compact) for smaller binary sizes, as +//! well ideal for embedded or web environments, where executable bloat can +//! be much more detrimental than performance. +//! +//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html //! //! # Getting Started //! +//! #### Parse API +//! +//! The main parsing API is [`parse`] and [`parse_partial`]. For example, +//! to parse a number from string, validating the entire input is a number: +//! //! ```rust -//! # #[cfg(all( -//! # feature = "parse-floats", -//! # feature = "parse-integers", -//! # feature = "write-floats", -//! # feature = "write-integers", -//! # ))] -//! # { -//! // Number to string -//! lexical::to_string(3.0); // "3.0", always has a fraction suffix. -//! lexical::to_string(3); // "3" -//! -//! // String to number. +//! # #[cfg(all(feature = "parse-floats", feature = "parse-integers"))] { //! let i: i32 = lexical::parse("3").unwrap(); // 3, auto-type deduction. //! let f: f32 = lexical::parse("3.5").unwrap(); // 3.5 //! let d = lexical::parse::("3.5"); // Ok(3.5), successful parse. -//! let d = lexical::parse::("3a"); // Err(Error(_)), failed to parse. +//! # } +//! ``` +//! +//! All `lexical` parsers are validating, they check the that entire input data +//! is correct, and stop parsing when invalid data is found, numerical overflow, +//! or other errors: +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "parse-integers"))] { +//! let r = lexical::parse::("256"); // Err(ErrorCode::Overflow.into()) +//! let r = lexical::parse::("1a5"); // Err(ErrorCode::InvalidDigit.into()) +//! # } +//! ``` +//! +//! For streaming APIs or those incrementally parsing data fed to a parser, +//! where the input data is known to be a float but where the float ends is +//! currently unknown, the partial parsers will both return the data it was +//! able to parse and the number of bytes processed: +//! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { +//! let r = lexical::parse_partial::("3a5"); // Ok((3, 1)) +//! # } +//! ``` +//! +//! #### Write API +//! +//! The main parsing API is [`to_string`]. For example, to write a number to +//! string: +//! +//! ```rust +//! # #[cfg(feature = "write-floats")] { +//! let value = lexical::to_string(15.1); +//! assert_eq!(value, "15.1"); //! # } //! ``` //! //! # Conversion API -#![cfg_attr(feature = "write", doc = " **To String**")] -#![cfg_attr(feature = "write", doc = "")] -#![cfg_attr(feature = "write", doc = " - [`to_string`]")] -#![cfg_attr(feature = "write", doc = " - [`to_string_with_options`]")] -//! -#![cfg_attr(feature = "write", doc = " **From String**")] -#![cfg_attr(feature = "write", doc = "")] -#![cfg_attr(feature = "parse", doc = " - [`parse`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_partial`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_with_options`]")] -#![cfg_attr(feature = "parse", doc = " - [`parse_partial_with_options`]")] +//! +//! This writes and parses numbers to and from a format identical to +//! Rust's [`parse`][`core-parse`] and [`write`][`core-write`]. +//! +//! [`core-parse`]: core::str::FromStr::from_str +//! [`core-write`]: core::fmt::Display::fmt +//! +//! +#![cfg_attr(feature = "write", doc = "- [`to_string`]: Write a number to string.")] +#![cfg_attr( + feature = "parse", + doc = " +- [`parse`]: Parse a number from string validating the complete string is a number. +- [`parse_partial`]: Parse a number from string returning the number and the number + of digits it was able to parse. +" +)] +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats"))] { +//! // parse +//! let f: f64 = lexical::parse(b"3.5").unwrap(); +//! assert_eq!(f, 3.5); +//! +//! let (f, count): (f64, usize) = lexical::parse_partial(b"3.5").unwrap(); +//! assert_eq!(f, 3.5); +//! assert_eq!(count, 3); +//! +//! // write +//! let value = lexical::to_string(f); +//! assert_eq!(value, "3.5"); +//! # } +//! ``` +//! +//! # Options/Formatting API +//! +//! Each number parser and writer contains extensive formatting control +//! through options and [`mod@format`] specifications, including digit +//! [`separator`] support (that is, numbers such as `1_2__3.4_5`), if +//! integral, fractional, or any significant digits are required, if to +//! disable parsing or writing of non-finite values, if `+` signs are +//! invalid or required, and much more. +//! +//! [`separator`]: NumberFormat::digit_separator +//! +//! +#![cfg_attr( + all(feature = "write", feature = "floats"), + doc = "[`nan_string`]: WriteFloatOptionsBuilder::nan_string" +)] +#![cfg_attr( + all(not(feature = "write"), feature = "parse", feature = "floats"), + doc = "[`nan_string`]: ParseFloatOptionsBuilder::nan_string" +)] +#![cfg_attr( + any(not(feature = "floats"), all(not(feature = "write"), not(feature = "parse"))), + doc = "[`nan_string`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.nan_string" +)] +//! +//! +#![cfg_attr( + feature = "write", + doc = "- [`to_string_with_options`]: Write a number to string using custom formatting options." +)] +#![cfg_attr( + feature = "parse", + doc = " +- [`parse_with_options`]: Parse a number from string using custom formatting options, + validating the complete string is a number. +- [`parse_partial_with_options`]: Parse a number from string using custom formatting + options, returning the number and the number of digits it was able to parse. +" +)] +//! +//! Some options, such as custom string representations of non-finite +//! floats (such as [`NaN`][`nan_string`]), are available without the +//! [`format`](crate#format) feature. For more comprehensive examples, see the +//! [`format`](#format) and [Comprehensive Configuration] sections +//! below. +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "format"))] { +//! use lexical::{format, parse_float_options, write_float_options}; +//! +//! // parse +//! let f: f64 = lexical::parse_with_options::<_, _, { format::JSON }>( +//! "3.5", +//! &parse_float_options::JSON +//! ).unwrap(); +//! +//! // write +//! let value = lexical::to_string_with_options::<_, { format::JSON }>( +//! f, +//! &write_float_options::JSON +//! ); +//! assert_eq!(value, "3.5"); +//! # } +//! ``` +//! +//! [Comprehensive Configuration]: #comprehensive-configuration //! //! # Features //! @@ -54,36 +176,66 @@ //! may be build with `--all-features` without issue. The following features //! are enabled by default: //! -//! * `std` -//! * `write-integers` -//! * `write-floats` -//! * `parse-integers` -//! * `parse-floats` -//! -//! A complete description of supported features includes: +//! * `write-integers` (Default) - Enable writing of integers. +//! * `write-floats` (Default) - Enable writing of floats. +//! * `parse-integers` (Default) - Enable parsing of integers. +//! * `parse-floats` (Default) - Enable parsing of floats. +//! * `radix` - Add support for strings of any radix. +//! * `compact` - Reduce code size at the cost of performance. +//! * `format` - Add support for custom number formatting. +//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and +//! [`bf16`][`brain-float`] floats. +//! * `std` (Default) - Disable to allow use in a [`no_std`] environment. //! -//! #### std +//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format +//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format //! -//! Enable use of the standard library. Currently, the standard library -//! is not used for any functionality, and may be disabled without any -//! change in functionality on stable. +//! A complete description of supported features includes: //! //! #### write-integers //! //! Enable support for writing integers to string. //! +//! ```rust +//! # #[cfg(feature = "write-integers")] { +//! let value = lexical::to_string(1234u64); +//! assert_eq!(value, "1234"); +//! # } +//! ``` +//! //! #### write-floats //! //! Enable support for writing floating-point numbers to string. //! +//! ```rust +//! # #[cfg(feature = "write-floats")] { +//! let value = lexical::to_string(1.234f64); +//! assert_eq!(value, "1.234"); +//! # } +//! ``` +//! //! #### parse-integers //! //! Enable support for parsing integers from string. //! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { +//! let f: i64 = lexical::parse("1234").unwrap(); +//! assert_eq!(f, 1234); +//! # } +//! ``` +//! //! #### parsing-floats //! //! Enable support for parsing floating-point numbers from string. //! +//! ```rust +//! # #[cfg(feature = "parse-integers")] { +//! let f: f64 = lexical::parse("1.234").unwrap(); +//! assert_eq!(f, 1.234); +//! # } +//! ``` +//! //! #### format //! //! Adds support for the entire format API (using [`NumberFormatBuilder`]). @@ -110,50 +262,100 @@ //! //! ```rust //! # #[cfg(all(feature = "parse-floats", feature = "format"))] { -//! use lexical_core::{format, parse_with_options, ParseFloatOptions, Result}; +//! use lexical::{format, parse_with_options, ParseFloatOptions, Result}; //! //! fn parse_json_float>(bytes: Bytes) -> Result { -//! let options = ParseFloatOptions::new(); -//! parse_with_options::<_, { format::JSON }>(bytes.as_ref(), &options) +//! const OPTIONS: ParseFloatOptions = ParseFloatOptions::new(); +//! parse_with_options::<_, _, { format::JSON }>(bytes.as_ref(), &OPTIONS) //! } //! # } //! ``` //! -//! See the [Number Format](#number-format) section below for more information. +//! Enabling the [`format`](crate#format) API significantly increases compile +//! times, however, it enables a large amount of customization in how floats are +//! written. //! //! #### power-of-two //! -//! Enable doing numeric conversions to and from strings with power-of-two -//! radixes. This avoids most of the overhead and binary bloat of the radix -//! feature, while enabling support for the most commonly-used radixes. +//! Enable doing numeric conversions to and from strings radixes that are powers +//! of two, that is, `2`, `4`, `8`, `16`, and `32`. This avoids most of the +//! overhead and binary bloat of the [`radix`](#radix) feature, while enabling +//! support for the most commonly-used radixes. +//! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "power-of-two"))] { +//! use lexical::{ +//! ParseFloatOptions, +//! WriteFloatOptions, +//! NumberFormatBuilder +//! }; +//! +//! // parse +//! const BINARY: u128 = NumberFormatBuilder::binary(); +//! let value = "1.0011101111100111011011001000101101000011100101011"; +//! let f: f64 = lexical::parse_with_options::<_, _, { BINARY }>( +//! value, +//! &ParseFloatOptions::new() +//! ).unwrap(); +//! +//! // write +//! let result = lexical::to_string_with_options::<_, { BINARY }>( +//! f, +//! &WriteFloatOptions::new() +//! ); +//! assert_eq!(result, value); +//! # } +//! ``` //! //! #### radix //! //! Enable doing numeric conversions to and from strings for all radixes. -//! This requires substantially more static storage than `power-of-two`, -//! and increases compile times by a fair amount, but can be quite useful +//! This requires more static storage than [`power-of-two`](#power-of-two), +//! and increases compile times, but can be quite useful //! for esoteric programming languages which use duodecimal floats, for //! example. //! +//! ```rust +//! # #[cfg(all(feature = "parse-floats", feature = "write-floats", feature = "radix"))] { +//! # use core::str; +//! use lexical::{ +//! ParseFloatOptions, +//! WriteFloatOptions, +//! NumberFormatBuilder +//! }; +//! +//! // parse +//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12); +//! let value = "1.29842830A44BAA2"; +//! let f: f64 = lexical::parse_with_options::<_, _, { FORMAT }>( +//! value, +//! &ParseFloatOptions::new() +//! ).unwrap(); +//! +//! // write +//! let result = lexical::to_string_with_options::<_, { FORMAT }>( +//! f, +//! &WriteFloatOptions::new() +//! ); +//! assert_eq!(result, value); +//! # } +//! ``` +//! //! #### compact //! //! Reduce the generated code size at the cost of performance. This minimizes //! the number of static tables, inlining, and generics used, drastically //! reducing the size of the generated binaries. //! -//! #### safe +//! #### std //! -//! This replaces most unchecked indexing, required in cases where the -//! compiler cannot elide the check, with checked indexing. However, -//! it does not fully replace all unsafe behavior with safe behavior. -//! To minimize the risk of undefined behavior and out-of-bounds reads/writers, -//! extensive edge-cases, property-based tests, and fuzzing is done with both -//! the safe feature enabled and disabled, with the tests verified by Miri -//! and Valgrind. +//! Enable use of the standard library. Currently, the standard library +//! is not used, and may be disabled without any change in functionality +//! on stable. //! -//! # Configuration API +//! # Comprehensive Configuration //! -//! Lexical provides two main levels of configuration: +//! `lexical` provides two main levels of configuration: //! - The [`NumberFormatBuilder`], creating a packed struct with custom //! formatting options. //! - The Options API. @@ -167,7 +369,7 @@ //! - The radix for the exponent base (default `10`). //! - The radix for the exponent digits (default `10`). //! -//! When the `format` feature is enabled, numerous other syntax and +//! When the [`format`](#format) feature is enabled, numerous other syntax and //! digit separator flags are enabled, including: //! - A digit separator character, to group digits for increased legibility. //! - Whether leading, trailing, internal, and consecutive digit separators are @@ -178,22 +380,87 @@ //! //! Many pre-defined constants therefore exist to simplify common use-cases, //! including: -//! - `JSON`, `XML`, `TOML`, `YAML`, `SQLite`, and many more. -//! - `Rust`, `Python`, `C#`, `FORTRAN`, `COBOL` literals and strings, and many -//! more. +//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more. +//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings, +//! and many more. +//! +//! For a list of all supported fields, see +//! [Fields][NumberFormatBuilder#fields-1]. +//! +//! +#![cfg_attr( + feature = "format", + doc = " +[`JSON`]: format::JSON +[`XML`]: format::XML +[`TOML`]: format::TOML +[`YAML`]: format::YAML +[`SQLite`]: format::SQLITE +[`Rust`]: format::RUST_LITERAL +[`Python`]: format::PYTHON_LITERAL +[`C#`]: format::CSHARP_LITERAL +[`FORTRAN`]: format::FORTRAN_LITERAL +[`COBOL`]: format::COBOL_LITERAL +" +)] +#![cfg_attr( + not(feature = "format"), + doc = " +[`JSON`]: https://docs.rs/lexical/latest/lexical/format/constant.JSON.html +[`XML`]: https://docs.rs/lexical/latest/lexical/format/constant.XML.html +[`TOML`]: https://docs.rs/lexical/latest/lexical/format/constant.TOML.html +[`YAML`]: https://docs.rs/lexical/latest/lexical/format/constant.YAML.html +[`SQLite`]: https://docs.rs/lexical/latest/lexical/format/constant.SQLITE.html +[`Rust`]: https://docs.rs/lexical/latest/lexical/format/constant.RUST_LITERAL.html +[`Python`]: https://docs.rs/lexical/latest/lexical/format/constant.PYTHON_LITERAL.html +[`C#`]: https://docs.rs/lexical/latest/lexical/format/constant.CSHARP_LITERAL.html +[`FORTRAN`]: https://docs.rs/lexical/latest/lexical/format/constant.FORTRAN_LITERAL.html +[`COBOL`]: https://docs.rs/lexical/latest/lexical/format/constant.COBOL_LITERAL.html +" +)] //! //! ## Options API //! //! The Options API provides high-level options to specify number parsing //! or writing, options not intrinsically tied to a number format. //! For example, the Options API provides: -//! - The exponent character (default `b'e'`, or `b'^'`). -//! - The decimal point character (default `b'.'`). -//! - Custom `NaN`, `Infinity` string representations. -//! - Whether to trim the fraction component from integral floats. -//! - The exponent break point for scientific notation. -//! - The maximum and minimum number of significant digits to write. -//! - The rounding mode when truncating significant digits while writing. +//! +//! - The [`exponent`][`write-float-exponent`] character (defaults to `b'e'` or `b'^'`, depending on the radix). +//! - The [`decimal point`][`write-float-decimal_point`] character (defaults to `b'.'`). +//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string +//! [`representations`][`write-float-nan_string`]. +//! - Whether to [`trim`][`write-float-trim_floats`] the fraction component from integral floats. +//! - The exponent [`break-point`][`write-float-positive_exponent_break`] for scientific notation. +//! - The [`maximum`][`write-float-max_significant_digits`] and [`minimum`][`write-float-min_significant_digits`] number of significant digits to write. +//! - The rounding [`mode`][`write-float-round_mode`] when truncating significant digits while writing. +//! +//! +#![cfg_attr( + feature = "write-floats", + doc = " +[`write-float-exponent`]: WriteFloatOptionsBuilder::exponent +[`write-float-decimal_point`]: WriteFloatOptionsBuilder::decimal_point +[`write-float-nan_string`]: WriteFloatOptionsBuilder::nan_string +[`write-float-trim_floats`]: WriteFloatOptionsBuilder::trim_floats +[`write-float-positive_exponent_break`]: WriteFloatOptionsBuilder::positive_exponent_break +[`write-float-max_significant_digits`]: WriteFloatOptionsBuilder::max_significant_digits +[`write-float-min_significant_digits`]: WriteFloatOptionsBuilder::min_significant_digits +[`write-float-round_mode`]: WriteFloatOptionsBuilder::round_mode +" +)] +#![cfg_attr( + not(feature = "write-floats"), + doc = " +[`write-float-exponent`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.exponent +[`write-float-decimal_point`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.decimal_point +[`write-float-nan_string`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.nan_string +[`write-float-trim_floats`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.trim_floats +[`write-float-positive_exponent_break`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.positive_exponent_break +[`write-float-max_significant_digits`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.max_significant_digits +[`write-float-min_significant_digits`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.min_significant_digits +[`write-float-round_mode`]: https://docs.rs/lexical/latest/lexical/struct.WriteFloatOptionsBuilder.html#method.round_mode +" +)] //! //! The available options are: #![cfg_attr(feature = "parse-floats", doc = " - [`ParseFloatOptions`]")] @@ -202,52 +469,81 @@ #![cfg_attr(feature = "write-integers", doc = " - [`WriteIntegerOptions`]")] //! //! In addition, pre-defined constants for each category of options may -//! be found in their respective modules. +//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`]. +//! +//! +#![cfg_attr(feature = "parse-floats", doc = "[`JSON-OPTS`]: parse_float_options::JSON")] +#![cfg_attr( + not(feature = "parse-floats"), + doc = "[`JSON-OPTS`]: https://docs.rs/lexical/latest/lexical/parse_float_options/constant.JSON.html" +)] //! -//! ## Example +//! ## Examples //! //! An example of creating your own options to parse European-style //! numbers (which use commas as decimal points, and periods as digit //! separators) is as follows: //! //! ``` -//! # pub fn main() { -//! # #[cfg(all(feature = "parse_floats", feature = "format"))] { +//! # #[cfg(all(feature = "parse-floats", feature = "format"))] { +//! # use core::num; //! // This creates a format to parse a European-style float number. //! // The decimal point is a comma, and the digit separators (optional) //! // are periods. //! const EUROPEAN: u128 = lexical::NumberFormatBuilder::new() -//! .digit_separator(b'.') -//! .build() -//! .unwrap(); -//! let options = lexical_core::ParseFloatOptions::builder() +//! .digit_separator(num::NonZeroU8::new(b'.')) +//! .build_strict(); +//! const COMMA_OPTIONS: lexical::ParseFloatOptions = lexical::ParseFloatOptions::builder() //! .decimal_point(b',') -//! .build() -//! .unwrap(); +//! .build_strict(); //! assert_eq!( -//! lexical::parse_with_options::("300,10", &options), +//! lexical::parse_with_options::("300,10", &COMMA_OPTIONS), //! Ok(300.10) //! ); //! //! // Another example, using a pre-defined constant for JSON. //! const JSON: u128 = lexical::format::JSON; -//! let options = lexical::ParseFloatOptions::new(); +//! const JSON_OPTIONS: lexical::ParseFloatOptions = lexical::ParseFloatOptions::new(); //! assert_eq!( -//! lexical::parse_with_options::("0e1", &options), +//! lexical::parse_with_options::("0e1", &JSON_OPTIONS), //! Ok(0.0) //! ); //! assert_eq!( -//! lexical::parse_with_options::("1E+2", &options), +//! lexical::parse_with_options::("1E+2", &JSON_OPTIONS), //! Ok(100.0) //! ); //! # } -//! # } //! ``` //! //! # Version Support //! -//! The minimum, standard, required version is 1.63.0, for const generic -//! support. Older versions of lexical support older Rust versions. +//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for +//! const generic support. Older versions of lexical support older Rust +//! versions. +//! +//! # Algorithms +//! +//! - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Algorithm.md) +//! - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Algorithm.md) +//! - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md) +//! - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md) +//! +//! # Benchmarks +//! +//! - [Parsing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-float/docs/Benchmarks.md) +//! - [Parsing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-parse-integer/docs/Benchmarks.md) +//! - [Writing Floats](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Benchmarks.md) +//! - [Writing Integers](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md) +//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks) +//! +//! A comprehensive analysis of lexical commits and their performance can be +//! found in [benchmarks]. +//! +//! # Design +//! +//! - [Binary Size](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BinarySize.md) +//! - [Build Timings](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/BuildTimings.md) +//! - [Digit Separators](https://github.com/Alexhuszagh/rust-lexical/blob/main/docs/DigitSeparators.md) //! //! # Safety //! @@ -257,25 +553,57 @@ //! and `lexical-write-integer`) could cause those safety invariants to //! be broken. //! -//! [`to_string`]: fn.to_string.html -//! [`to_string_with_options`]: fn.to_string_with_options.html -//! [`write_with_options`]: crate::write_with_options -//! [`parse`]: crate::parse -//! [`parse_partial`]: crate::parse_partial -//! [`parse_with_options`]: crate::parse_with_options -//! [`parse_partial_with_options`]: crate::parse_partial_with_options +//! +#![cfg_attr( + feature = "write", + doc = " +[`to_string`]: crate::to_string +[`to_string_with_options`]: crate::to_string_with_options +" +)] +#![cfg_attr( + not(feature = "write"), + doc = " +[`to_string`]: https://docs.rs/lexical/latest/lexical/fn.to_string.html +[`to_string_with_options`]: https://docs.rs/lexical/latest/lexical/fn.to_string_with_options.html +" +)] +#![cfg_attr( + feature = "parse", + doc = " +[`parse`]: crate::parse +[`parse_partial`]: crate::parse_partial +[`parse_with_options`]: crate::parse_with_options +[`parse_partial_with_options`]: crate::parse_partial_with_options +" +)] +#![cfg_attr( + not(feature = "parse"), + doc = " +[`parse`]: https://docs.rs/lexical/latest/lexical/fn.parse.html +[`parse_partial`]: https://docs.rs/lexical/latest/lexical/fn.parse_partial.html +[`parse_with_options`]: https://docs.rs/lexical/latest/lexical/fn.parse_with_options.html +[`parse_partial_with_options`]: https://docs.rs/lexical/latest/lexical/fn.parse_partial_with_options.html +" +)] +//! +//! +#![cfg_attr(feature = "parse-floats", doc = "[`ParseFloatOptions`]: crate::ParseFloatOptions")] +#![cfg_attr(feature = "parse-integers", doc = "[`ParseIntegerOptions`]: crate::ParseIntegerOptions")] +#![cfg_attr(feature = "write-floats", doc = "[`WriteFloatOptions`]: crate::WriteFloatOptions")] +#![cfg_attr(feature = "write-integers", doc = "[`WriteIntegerOptions`]: crate::WriteIntegerOptions")] //! //! [`NumberFormatBuilder`]: crate::NumberFormatBuilder -//! [`ParseFloatOptions`]: crate::ParseFloatOptions -//! [`ParseIntegerOptions`]: crate::ParseIntegerOptions -//! [`WriteFloatOptions`]: crate::WriteFloatOptions -//! [`WriteIntegerOptions`]: crate::WriteIntegerOptions +//! [benchmarks]: https://github.com/Alexhuszagh/lexical-benchmarks +//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html // We want to have the same safety guarantees as Rust core, // so we allow unused unsafe to clearly document safety guarantees. #![allow(unused_unsafe)] #![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny( clippy::doc_markdown, clippy::unnecessary_safety_comment, @@ -300,6 +628,7 @@ // we use this for inline formatting for unsafe blocks clippy::semicolon_inside_block, )] +#![cfg_attr(rustfmt, rustfmt_skip)] // reason = "this simplifies our imports" // Ensure our features are properly enabled. This means no parse without // parse support, etc. @@ -329,31 +658,58 @@ extern crate alloc; #[cfg(feature = "write")] use alloc::string::String; -pub use lexical_core::format::{self, format_error, format_is_valid, NumberFormatBuilder}; -#[cfg(feature = "parse")] +// Re-exports pub use lexical_core::Error; -#[cfg(feature = "parse")] -pub use lexical_core::ParseOptions; -#[cfg(feature = "parse")] pub use lexical_core::Result; -#[cfg(feature = "write")] -pub use lexical_core::WriteOptions; + +pub use lexical_core::format::{ + self, + // FIXME: Do not export in the next breaking release. + format_error, + // FIXME: Do not export in the next breaking release. + format_is_valid, + NumberFormat, + NumberFormatBuilder, +}; + #[cfg(feature = "f16")] pub use lexical_core::{bf16, f16}; + +// PARSE + +#[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +pub use lexical_core::ParseOptions; + +#[cfg(feature = "parse")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +pub use lexical_core::{FromLexical, FromLexicalWithOptions}; + #[cfg(feature = "parse-floats")] pub use lexical_core::{parse_float_options, ParseFloatOptions, ParseFloatOptionsBuilder}; + #[cfg(feature = "parse-integers")] pub use lexical_core::{parse_integer_options, ParseIntegerOptions, ParseIntegerOptionsBuilder}; + +// WRITE + +#[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +pub use lexical_core::WriteOptions; + +#[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +pub use lexical_core::{ToLexical, ToLexicalWithOptions}; + +#[cfg(feature = "write")] +#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +pub use lexical_core::{FormattedSize, BUFFER_SIZE}; + #[cfg(feature = "write-floats")] pub use lexical_core::{write_float_options, WriteFloatOptions, WriteFloatOptionsBuilder}; + #[cfg(feature = "write-integers")] pub use lexical_core::{write_integer_options, WriteIntegerOptions, WriteIntegerOptionsBuilder}; -#[cfg(feature = "write")] -pub use lexical_core::{FormattedSize, BUFFER_SIZE}; -#[cfg(feature = "parse")] -pub use lexical_core::{FromLexical, FromLexicalWithOptions}; -#[cfg(feature = "write")] -pub use lexical_core::{ToLexical, ToLexicalWithOptions}; // NOTE: We cannot just use an uninitialized vector with excess capacity and // then use read-assign rather than `ptr::write` or `MaybeUninit.write` to @@ -386,11 +742,8 @@ pub use lexical_core::{ToLexical, ToLexicalWithOptions}; /// # Examples /// /// ```rust -/// # extern crate lexical; -/// # pub fn main() { /// assert_eq!(lexical::to_string(5), "5"); /// assert_eq!(lexical::to_string(0.0), "0.0"); -/// # } /// ``` #[inline] #[cfg(feature = "write")] @@ -415,18 +768,16 @@ pub fn to_string(n: N) -> String { /// # Examples /// /// ```rust -/// # pub fn main() { /// const FORMAT: u128 = lexical::format::STANDARD; -/// let options = lexical::WriteFloatOptions::builder() +/// const OPTIONS: lexical::WriteFloatOptions = lexical::WriteFloatOptions::builder() /// .trim_floats(true) -/// .build() -/// .unwrap(); -/// assert_eq!(lexical::to_string_with_options::<_, FORMAT>(0.0, &options), "0"); -/// assert_eq!(lexical::to_string_with_options::<_, FORMAT>(123.456, &options), "123.456"); -/// # } +/// .build_strict(); +/// assert_eq!(lexical::to_string_with_options::<_, FORMAT>(0.0, &OPTIONS), "0"); +/// assert_eq!(lexical::to_string_with_options::<_, FORMAT>(123.456, &OPTIONS), "123.456"); /// ``` #[inline] #[cfg(feature = "write")] +#[allow(deprecated)] // reason = "allow the user of `buffer_size`" pub fn to_string_with_options( n: N, options: &N::Options, @@ -456,9 +807,7 @@ pub fn to_string_with_options( /// # Examples /// /// ```rust -/// # extern crate lexical; /// # use lexical::Error; -/// # pub fn main() { /// // Create our error. /// fn error(r: lexical::Result) -> Error { /// r.err().unwrap() @@ -479,7 +828,6 @@ pub fn to_string_with_options( /// assert_eq!(lexical::parse::(b"1."), Ok(1.0)); /// # assert_eq!(lexical::parse::(b"5.002868148396374"), Ok(5.002868148396374)); /// # assert_eq!(lexical::parse::(b"5.002868148396374"), Ok(5.002868148396374)); -/// # } /// ``` #[inline] #[cfg(feature = "parse")] @@ -500,9 +848,6 @@ pub fn parse>(bytes: Bytes) -> Result { /// # Examples /// /// ```rust -/// # extern crate lexical; -/// # pub fn main() { -/// /// // String overloads /// assert_eq!(lexical::parse_partial::("5"), Ok((5, 1))); /// assert_eq!(lexical::parse_partial::("1a"), Ok((1, 1))); @@ -518,7 +863,6 @@ pub fn parse>(bytes: Bytes) -> Result { /// assert_eq!(lexical::parse_partial::(b"1."), Ok((1.0, 2))); /// # assert_eq!(lexical::parse_partial::(b"5.002868148396374"), Ok((5.002868148396374, 17))); /// # assert_eq!(lexical::parse_partial::(b"5.002868148396374"), Ok((5.002868148396374, 17))); -/// # } /// ``` #[inline] #[cfg(feature = "parse")] @@ -544,17 +888,14 @@ pub fn parse_partial>(bytes: Bytes) -> Result /// # Examples /// /// ```rust -/// # pub fn main() { /// const FORMAT: u128 = lexical::format::STANDARD; -/// let options = lexical::ParseFloatOptions::builder() +/// const OPTIONS: lexical::ParseFloatOptions = lexical::ParseFloatOptions::builder() /// .exponent(b'^') /// .decimal_point(b',') -/// .build() -/// .unwrap(); -/// assert_eq!(lexical::parse_with_options::("0", &options), Ok(0.0)); -/// assert_eq!(lexical::parse_with_options::("1,2345", &options), Ok(1.2345)); -/// assert_eq!(lexical::parse_with_options::("1,2345^4", &options), Ok(12345.0)); -/// # } +/// .build_strict(); +/// assert_eq!(lexical::parse_with_options::("0", &OPTIONS), Ok(0.0)); +/// assert_eq!(lexical::parse_with_options::("1,2345", &OPTIONS), Ok(1.2345)); +/// assert_eq!(lexical::parse_with_options::("1,2345^4", &OPTIONS), Ok(12345.0)); /// ``` #[inline] #[cfg(feature = "parse")] @@ -587,17 +928,14 @@ pub fn parse_with_options, const F /// # Examples /// /// ```rust -/// # pub fn main() { /// const FORMAT: u128 = lexical::format::STANDARD; -/// let options = lexical::ParseFloatOptions::builder() +/// const OPTIONS: lexical::ParseFloatOptions = lexical::ParseFloatOptions::builder() /// .exponent(b'^') /// .decimal_point(b',') -/// .build() -/// .unwrap(); -/// assert_eq!(lexical::parse_partial_with_options::("0", &options), Ok((0.0, 1))); -/// assert_eq!(lexical::parse_partial_with_options::("1,2345", &options), Ok((1.2345, 6))); -/// assert_eq!(lexical::parse_partial_with_options::("1,2345^4", &options), Ok((12345.0, 8))); -/// # } +/// .build_strict(); +/// assert_eq!(lexical::parse_partial_with_options::("0", &OPTIONS), Ok((0.0, 1))); +/// assert_eq!(lexical::parse_partial_with_options::("1,2345", &OPTIONS), Ok((1.2345, 6))); +/// assert_eq!(lexical::parse_partial_with_options::("1,2345^4", &OPTIONS), Ok((12345.0, 8))); /// ``` #[inline] #[cfg(feature = "parse")] diff --git a/lexical/tests/api_tests.rs b/lexical/tests/api_tests.rs index efdb2751..e3129e13 100644 --- a/lexical/tests/api_tests.rs +++ b/lexical/tests/api_tests.rs @@ -2,18 +2,18 @@ #[cfg(feature = "write-integers")] fn integer_to_string_test() { assert_eq!(lexical::to_string(12345u32), "12345"); - let options = lexical::WriteIntegerOptions::new(); + const OPTIONS: lexical::WriteIntegerOptions = lexical::WriteIntegerOptions::new(); const FORMAT: u128 = lexical::format::STANDARD; - assert_eq!(lexical::to_string_with_options::<_, FORMAT>(12345u32, &options), "12345"); + assert_eq!(lexical::to_string_with_options::<_, FORMAT>(12345u32, &OPTIONS), "12345"); } #[test] #[cfg(feature = "write-floats")] fn float_to_string_test() { assert_eq!(lexical::to_string(12345.0f32), "12345.0"); - let options = lexical::WriteFloatOptions::new(); + const OPTIONS: lexical::WriteFloatOptions = lexical::WriteFloatOptions::new(); const FORMAT: u128 = lexical::format::STANDARD; - assert_eq!(lexical::to_string_with_options::<_, FORMAT>(12345.0f32, &options), "12345.0"); + assert_eq!(lexical::to_string_with_options::<_, FORMAT>(12345.0f32, &OPTIONS), "12345.0"); } #[test] @@ -22,11 +22,11 @@ fn string_to_integer_test() { assert_eq!(lexical::parse("12345"), Ok(12345u32)); assert_eq!(lexical::parse_partial("12345"), Ok((12345u32, 5))); - let options = lexical::ParseIntegerOptions::new(); + const OPTIONS: lexical::ParseIntegerOptions = lexical::ParseIntegerOptions::new(); const FORMAT: u128 = lexical::format::STANDARD; - assert_eq!(lexical::parse_with_options::<_, _, FORMAT>("12345", &options), Ok(12345u32)); + assert_eq!(lexical::parse_with_options::<_, _, FORMAT>("12345", &OPTIONS), Ok(12345u32)); assert_eq!( - lexical::parse_partial_with_options::<_, _, FORMAT>("12345", &options), + lexical::parse_partial_with_options::<_, _, FORMAT>("12345", &OPTIONS), Ok((12345u32, 5)) ); } @@ -37,11 +37,11 @@ fn string_to_float_test() { assert_eq!(lexical::parse("12345.0"), Ok(12345.0f32)); assert_eq!(lexical::parse_partial("12345.0"), Ok((12345.0f32, 7))); - let options = lexical::ParseFloatOptions::new(); + const OPTIONS: lexical::ParseFloatOptions = lexical::ParseFloatOptions::new(); const FORMAT: u128 = lexical::format::STANDARD; - assert_eq!(lexical::parse_with_options::<_, _, FORMAT>("12345.0", &options), Ok(12345.0f32)); + assert_eq!(lexical::parse_with_options::<_, _, FORMAT>("12345.0", &OPTIONS), Ok(12345.0f32)); assert_eq!( - lexical::parse_partial_with_options::<_, _, FORMAT>("12345.0", &options), + lexical::parse_partial_with_options::<_, _, FORMAT>("12345.0", &OPTIONS), Ok((12345.0f32, 7)) ); } diff --git a/scripts/docs.py b/scripts/docs.py new file mode 100644 index 00000000..88da36ae --- /dev/null +++ b/scripts/docs.py @@ -0,0 +1,134 @@ +''' + docs + ==== + + Script to validate the generated documentation + + This also validates the TOML files and any links inside the docs. +''' + +import html.parser +import time +import urllib.error +import urllib.request +from pathlib import Path + +# This is a hack for older Python versions +# Remove once gh actions drops support for Python < 3.11 +try: + import tomllib +except ImportError: + import pip._vendor.tomli as tomllib + +home_dir = Path(__file__).absolute().parent.parent +target_dir = home_dir / 'target' / 'doc' + + +class LinkParser(html.parser.HTMLParser): + '''Custom parser that looks for links within HTML.''' + + links: set[str] + processed: int + file: str + + def __init__(self) -> None: + super().__init__() + self.links = set() + self.processed = 0 + self.file = '' + + def feed(self, file: str, data: str) -> None: + '''Feed data to the underlying file.''' + self.file = file + super().feed(data) + + def handle_starttag(self, tag: str, attrs: list[tuple[str, str]]) -> None: + '''Find if it's a link and if so add the data.''' + + # these are all programmatically blocked, likely cause of our user agent + # they're stable links so it's fine + blocked = ( + 'https://sagemath.org/', + 'https://www.sagemath.org/', + 'https://www.java.com/en/', + 'https://coffeescript.org/', + ) + # skip our non-link tags and our local/script links + if tag != 'a': + return + attributes = dict(attrs) + assert 'href' in attributes + href = attributes['href'] + if not href.startswith(('https://', 'http://')): + return + if href.startswith(blocked): + return + + # try to avoid getting deny-listed or rate limited + self.processed += 1 + if href not in self.links: + try: + request = urllib.request.Request(href) + # spoof this to avoid getting blocked + request.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux i686)') + # NOTE: `crates.io` requires an `Accept: text/html` + # https://github.com/rust-lang/crates.io/issues/788 + if href.startswith(('https://crates.io', 'https://www.crates.io')): + request.add_header('Accept', 'text/html') + response = urllib.request.urlopen(request) + except urllib.error.HTTPError as error: + if error.code in (401, 403): + return + msg = f'Got an invalid href "{href}" with code "{error.code}" for file "{self.file}".' + raise ValueError(msg) + time.sleep(0.2) + code = response.code + if response.code != 200: + raise ValueError(f'Got an invalid href "{href}" with code "{code}" for file "{self.file}".') + self.links.add(href) + + if self.processed > 1 and self.processed % 200 == 0: + print(f'Processing link {self.processed}...') + + def handle_endtag(self, tag: str) -> None: + '''Handle the closing of a tag (ignored).''' + _ = tag + + def handle_data(self, data: str): + '''Handle any raw data (we ignore this).''' + _ = data + + +def main() -> None: + '''Run our validation code.''' + + # get all our toml files + for path in home_dir.rglob('**/*.toml'): + # Bug fixes for Docker on Windows. We don't want dups anyway. + if path.is_symlink(): + continue + print(f'Processing TOML file "{path.relative_to(home_dir)}"...') + # NOTE: This is a workaround for Python < 3.11, since `tomli` + # expects a `str` and `tomllib` expects `bytes` for `load`. + # Also, `resolve` is a bug fix for symlinks prior to 3.11. + # `as_posix()` is a bug fix for symbolic links on Windows docker. + with open(path.absolute().as_posix(), encoding='utf-8') as file: + data = file.read() + _ = tomllib.loads(data) + + # get all our links + parser = LinkParser() + for path in target_dir.rglob('**/*.html'): + # Bug fixes for Docker on Windows. We don't want dups anyway. + if path.is_symlink(): + continue + with path.open(encoding='utf-8') as file: + data = file.read() + parser.feed(path.name, data) + + # deduplicate and validate all our links + print(f'Processed and validated {len(parser.links)} links...') + + +if __name__ == '__main__': + main()