Skip to content

Commit 933a8da

Browse files
committed
Add logic for formats that may support only a few conversions.
This adds the following number format flags: - `supports_parsing_integers` - `supports_parsing_floats` - `supports_writing_integers` - `supports_writing_floats` If an operation is not supported with the format feature, then the code panics or returns an error immediately, which will always be resolved at compile time.
1 parent 67d3b3f commit 933a8da

File tree

13 files changed

+492
-33
lines changed

13 files changed

+492
-33
lines changed

lexical-parse-float/src/api.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ macro_rules! float_from_lexical {
5151
) -> lexical_util::result::Result<Self>
5252
{
5353
let format = NumberFormat::<{ FORMAT }> {};
54-
if !format.is_valid() {
54+
if !format.supports_parsing_floats() {
55+
return Err(Error::Unsupported);
56+
} else if !format.is_valid() {
5557
return Err(format.error());
5658
} else if !is_valid_options_punctuation(FORMAT, options.exponent(), options.decimal_point()) {
5759
return Err(Error::InvalidPunctuation);
@@ -65,6 +67,14 @@ macro_rules! float_from_lexical {
6567
options: &Self::Options,
6668
) -> lexical_util::result::Result<(Self, usize)>
6769
{
70+
let format = NumberFormat::<{ FORMAT }> {};
71+
if !format.supports_parsing_floats() {
72+
return Err(Error::Unsupported);
73+
} else if !format.is_valid() {
74+
return Err(format.error());
75+
} else if !is_valid_options_punctuation(FORMAT, options.exponent(), options.decimal_point()) {
76+
return Err(Error::InvalidPunctuation);
77+
}
6878
Self::parse_partial::<FORMAT>(bytes, options)
6979
}
7080
}

lexical-parse-float/tests/api_tests.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,3 +1275,35 @@ fn issue68_test() {
12751275
assert_eq!(f32::INFINITY, f32::from_lexical_with_options::<FORMAT>(hex, &OPTIONS).unwrap());
12761276
assert_eq!(f64::INFINITY, f64::from_lexical_with_options::<FORMAT>(hex, &OPTIONS).unwrap());
12771277
}
1278+
1279+
#[test]
1280+
#[cfg(feature = "format")]
1281+
fn unsupported_test() {
1282+
const FORMAT: u128 = NumberFormatBuilder::new().supports_parsing_floats(false).build_strict();
1283+
const OPTIONS: Options = Options::new();
1284+
1285+
let float = "12345.0";
1286+
let value = f64::from_lexical_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
1287+
assert_eq!(value, Err(Error::Unsupported));
1288+
1289+
let value = f64::from_lexical_partial_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
1290+
assert_eq!(value, Err(Error::Unsupported));
1291+
}
1292+
1293+
#[test]
1294+
#[cfg(feature = "format")]
1295+
fn supported_test() {
1296+
const FORMAT: u128 = NumberFormatBuilder::new()
1297+
.supports_parsing_integers(false)
1298+
.supports_writing_integers(false)
1299+
.supports_writing_floats(false)
1300+
.build_strict();
1301+
const OPTIONS: Options = Options::new();
1302+
1303+
let float = "12345.0";
1304+
let value = f64::from_lexical_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
1305+
assert_eq!(value, Ok(12345.0));
1306+
1307+
let value = f64::from_lexical_partial_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
1308+
assert_eq!(value, Ok((12345.0, 7)));
1309+
}

lexical-parse-integer/src/api.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#![doc(hidden)]
44

5+
use lexical_util::error::Error;
56
use lexical_util::format::{NumberFormat, STANDARD};
67
use lexical_util::{from_lexical, from_lexical_with_options};
78

@@ -42,7 +43,9 @@ macro_rules! integer_from_lexical {
4243
) -> lexical_util::result::Result<Self>
4344
{
4445
let format = NumberFormat::<{ FORMAT }> {};
45-
if !format.is_valid() {
46+
if !format.supports_parsing_integers() {
47+
return Err(Error::Unsupported);
48+
} else if !format.is_valid() {
4649
return Err(format.error());
4750
}
4851
Self::parse_complete::<FORMAT>(bytes, options)
@@ -55,7 +58,9 @@ macro_rules! integer_from_lexical {
5558
) -> lexical_util::result::Result<(Self, usize)>
5659
{
5760
let format = NumberFormat::<{ FORMAT }> {};
58-
if !format.is_valid() {
61+
if !format.supports_parsing_integers() {
62+
return Err(Error::Unsupported);
63+
} else if !format.is_valid() {
5964
return Err(format.error());
6065
}
6166
Self::parse_partial::<FORMAT>(bytes, options)

lexical-parse-integer/tests/api_tests.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,47 @@ fn base_prefix_and_suffix_test() {
357357
assert!(i32::from_lexical_with_options::<FORMAT>(b"+h", &OPTIONS).is_err());
358358
assert!(i32::from_lexical_with_options::<FORMAT>(b"+0x", &OPTIONS).is_err());
359359
}
360+
361+
#[test]
362+
#[cfg(feature = "format")]
363+
fn unsupported_test() {
364+
const FORMAT: u128 = NumberFormatBuilder::new().supports_parsing_integers(false).build_strict();
365+
const OPTIONS: Options = Options::new();
366+
367+
let integer = "12345";
368+
let value = i64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
369+
assert_eq!(value, Err(Error::Unsupported));
370+
371+
let value = i64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
372+
assert_eq!(value, Err(Error::Unsupported));
373+
374+
let value = u64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
375+
assert_eq!(value, Err(Error::Unsupported));
376+
377+
let value = u64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
378+
assert_eq!(value, Err(Error::Unsupported));
379+
}
380+
381+
#[test]
382+
#[cfg(feature = "format")]
383+
fn supported_test() {
384+
const FORMAT: u128 = NumberFormatBuilder::new()
385+
.supports_parsing_floats(false)
386+
.supports_writing_integers(false)
387+
.supports_writing_floats(false)
388+
.build_strict();
389+
const OPTIONS: Options = Options::new();
390+
391+
let integer = "12345";
392+
let value = i64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
393+
assert_eq!(value, Ok(12345));
394+
395+
let value = i64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
396+
assert_eq!(value, Ok((12345, 5)));
397+
398+
let value = u64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
399+
assert_eq!(value, Ok(12345));
400+
401+
let value = u64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
402+
assert_eq!(value, Ok((12345, 5)));
403+
}

lexical-util/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub enum Error {
9292
InvalidConsecutiveExponentDigitSeparator,
9393
/// Invalid flags were set without the format feature.
9494
InvalidFlags,
95+
/// If the operation is unsupported.
96+
Unsupported,
9597

9698
// OPTION ERRORS
9799
/// Invalid NaN string: must start with an `n` character.
@@ -184,6 +186,7 @@ impl Error {
184186
Self::InvalidConsecutiveFractionDigitSeparator => "'enabled consecutive digit separators in the fraction without setting a valid location'",
185187
Self::InvalidConsecutiveExponentDigitSeparator => "'enabled consecutive digit separators in the exponent without setting a valid location'",
186188
Self::InvalidFlags => "'invalid flags enabled without the format feature'",
189+
Self::Unsupported => "the desired operation is unsupported for this format",
187190

188191
// OPTION ERRORS
189192
Self::InvalidNanString => "'NaN string must started with `n`'",
@@ -249,6 +252,7 @@ impl Error {
249252
Self::InvalidConsecutiveFractionDigitSeparator => None,
250253
Self::InvalidConsecutiveExponentDigitSeparator => None,
251254
Self::InvalidFlags => None,
255+
Self::Unsupported => None,
252256

253257
// OPTION ERRORS
254258
Self::InvalidNanString => None,
@@ -314,6 +318,7 @@ impl Error {
314318
InvalidConsecutiveExponentDigitSeparator
315319
);
316320
is_error_type!(is_invalid_flags, InvalidFlags);
321+
is_error_type!(is_unsupported, Unsupported);
317322
is_error_type!(is_invalid_nan_string, InvalidNanString);
318323
is_error_type!(is_nan_string_too_long, NanStringTooLong);
319324
is_error_type!(is_invalid_inf_string, InvalidInfString);
@@ -411,6 +416,7 @@ impl fmt::Display for Error {
411416
format_message!(formatter, description)
412417
},
413418
Self::InvalidFlags => format_message!(formatter, description),
419+
Self::Unsupported => format_message!(formatter, description),
414420

415421
// OPTION ERRORS
416422
Self::InvalidNanString => options_message!(formatter, description),

lexical-util/src/feature_format.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,78 @@ impl<const FORMAT: u128> NumberFormat<FORMAT> {
642642
Self::REQUIRED_FRACTION_DIGITS_WITH_EXPONENT
643643
}
644644

645+
/// If the format supports parsing integers.
646+
///
647+
/// See [`supports_parsing_integers`][Self::supports_parsing_integers].
648+
pub const SUPPORTS_PARSING_INTEGERS: bool = from_flag!(FORMAT, SUPPORTS_PARSING_INTEGERS);
649+
650+
/// Get if the format supports parsing integers.
651+
///
652+
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
653+
/// to [`true`].
654+
///
655+
/// # Used For
656+
///
657+
/// - Parse Integer
658+
#[inline(always)]
659+
pub const fn supports_parsing_integers(&self) -> bool {
660+
Self::SUPPORTS_PARSING_INTEGERS
661+
}
662+
663+
/// If the format supports parsing floats.
664+
///
665+
/// See [`supports_parsing_floats`][Self::supports_parsing_floats].
666+
pub const SUPPORTS_PARSING_FLOATS: bool = from_flag!(FORMAT, SUPPORTS_PARSING_FLOATS);
667+
668+
/// Get if the format supports parsing floats.
669+
///
670+
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
671+
/// to [`true`].
672+
///
673+
/// # Used For
674+
///
675+
/// - Parse Float
676+
#[inline(always)]
677+
pub const fn supports_parsing_floats(&self) -> bool {
678+
Self::SUPPORTS_PARSING_FLOATS
679+
}
680+
681+
/// If the format supports writing integers.
682+
///
683+
/// See [`supports_writing_integers`][Self::supports_writing_integers].
684+
pub const SUPPORTS_WRITING_INTEGERS: bool = from_flag!(FORMAT, SUPPORTS_WRITING_INTEGERS);
685+
686+
/// Get if the format supports writing integers.
687+
///
688+
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
689+
/// to [`true`].
690+
///
691+
/// # Used For
692+
///
693+
/// - Write Integer
694+
#[inline(always)]
695+
pub const fn supports_writing_integers(&self) -> bool {
696+
Self::SUPPORTS_WRITING_INTEGERS
697+
}
698+
699+
/// If the format supports writing floats.
700+
///
701+
/// See [`supports_writing_floats`][Self::supports_writing_floats].
702+
pub const SUPPORTS_WRITING_FLOATS: bool = from_flag!(FORMAT, SUPPORTS_WRITING_FLOATS);
703+
704+
/// Get if the format supports writing floats.
705+
///
706+
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
707+
/// to [`true`].
708+
///
709+
/// # Used For
710+
///
711+
/// - Write Float
712+
#[inline(always)]
713+
pub const fn supports_writing_floats(&self) -> bool {
714+
Self::SUPPORTS_WRITING_FLOATS
715+
}
716+
645717
// DIGIT SEPARATOR FLAGS & MASKS
646718

647719
/// If digit separators are allowed between integer digits.

0 commit comments

Comments
 (0)