Skip to content

Commit

Permalink
Add logic for formats that may support only a few conversions.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Alexhuszagh committed Jan 12, 2025
1 parent 0cad692 commit f53fae0
Show file tree
Hide file tree
Showing 13 changed files with 492 additions and 33 deletions.
12 changes: 11 additions & 1 deletion lexical-parse-float/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ macro_rules! float_from_lexical {
) -> lexical_util::result::Result<Self>
{
let format = NumberFormat::<{ FORMAT }> {};
if !format.is_valid() {
if !format.supports_parsing_floats() {
return Err(Error::Unsupported);
} else if !format.is_valid() {
return Err(format.error());
} else if !is_valid_options_punctuation(FORMAT, options.exponent(), options.decimal_point()) {
return Err(Error::InvalidPunctuation);
Expand All @@ -65,6 +67,14 @@ macro_rules! float_from_lexical {
options: &Self::Options,
) -> lexical_util::result::Result<(Self, usize)>
{
let format = NumberFormat::<{ FORMAT }> {};
if !format.supports_parsing_floats() {
return Err(Error::Unsupported);
} else if !format.is_valid() {
return Err(format.error());
} else if !is_valid_options_punctuation(FORMAT, options.exponent(), options.decimal_point()) {
return Err(Error::InvalidPunctuation);
}
Self::parse_partial::<FORMAT>(bytes, options)
}
}
Expand Down
32 changes: 32 additions & 0 deletions lexical-parse-float/tests/api_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,3 +1275,35 @@ fn issue68_test() {
assert_eq!(f32::INFINITY, f32::from_lexical_with_options::<FORMAT>(hex, &OPTIONS).unwrap());
assert_eq!(f64::INFINITY, f64::from_lexical_with_options::<FORMAT>(hex, &OPTIONS).unwrap());
}

#[test]
#[cfg(feature = "format")]
fn unsupported_test() {
const FORMAT: u128 = NumberFormatBuilder::new().supports_parsing_floats(false).build_strict();
const OPTIONS: Options = Options::new();

let float = "12345.0";
let value = f64::from_lexical_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));

let value = f64::from_lexical_partial_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));
}

#[test]
#[cfg(feature = "format")]
fn supported_test() {
const FORMAT: u128 = NumberFormatBuilder::new()
.supports_parsing_integers(false)
.supports_writing_integers(false)
.supports_writing_floats(false)
.build_strict();
const OPTIONS: Options = Options::new();

let float = "12345.0";
let value = f64::from_lexical_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
assert_eq!(value, Ok(12345.0));

let value = f64::from_lexical_partial_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
assert_eq!(value, Ok((12345.0, 7)));
}
9 changes: 7 additions & 2 deletions lexical-parse-integer/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![doc(hidden)]

use lexical_util::error::Error;
use lexical_util::format::{NumberFormat, STANDARD};
use lexical_util::{from_lexical, from_lexical_with_options};

Expand Down Expand Up @@ -42,7 +43,9 @@ macro_rules! integer_from_lexical {
) -> lexical_util::result::Result<Self>
{
let format = NumberFormat::<{ FORMAT }> {};
if !format.is_valid() {
if !format.supports_parsing_integers() {
return Err(Error::Unsupported);
} else if !format.is_valid() {
return Err(format.error());
}
Self::parse_complete::<FORMAT>(bytes, options)
Expand All @@ -55,7 +58,9 @@ macro_rules! integer_from_lexical {
) -> lexical_util::result::Result<(Self, usize)>
{
let format = NumberFormat::<{ FORMAT }> {};
if !format.is_valid() {
if !format.supports_parsing_integers() {
return Err(Error::Unsupported);
} else if !format.is_valid() {
return Err(format.error());
}
Self::parse_partial::<FORMAT>(bytes, options)
Expand Down
44 changes: 44 additions & 0 deletions lexical-parse-integer/tests/api_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,47 @@ fn base_prefix_and_suffix_test() {
assert!(i32::from_lexical_with_options::<FORMAT>(b"+h", &OPTIONS).is_err());
assert!(i32::from_lexical_with_options::<FORMAT>(b"+0x", &OPTIONS).is_err());
}

#[test]
#[cfg(feature = "format")]
fn unsupported_test() {
const FORMAT: u128 = NumberFormatBuilder::new().supports_parsing_integers(false).build_strict();
const OPTIONS: Options = Options::new();

let integer = "12345";
let value = i64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));

let value = i64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));

let value = u64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));

let value = u64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Err(Error::Unsupported));
}

#[test]
#[cfg(feature = "format")]
fn supported_test() {
const FORMAT: u128 = NumberFormatBuilder::new()
.supports_parsing_floats(false)
.supports_writing_integers(false)
.supports_writing_floats(false)
.build_strict();
const OPTIONS: Options = Options::new();

let integer = "12345";
let value = i64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Ok(12345));

let value = i64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Ok((12345, 5)));

let value = u64::from_lexical_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Ok(12345));

let value = u64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Ok((12345, 5)));
}
6 changes: 6 additions & 0 deletions lexical-util/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub enum Error {
InvalidConsecutiveExponentDigitSeparator,
/// Invalid flags were set without the format feature.
InvalidFlags,
/// If the operation is unsupported.
Unsupported,

// OPTION ERRORS
/// Invalid NaN string: must start with an `n` character.
Expand Down Expand Up @@ -184,6 +186,7 @@ impl Error {
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'",
Self::Unsupported => "the desired operation is unsupported for this format",

// OPTION ERRORS
Self::InvalidNanString => "'NaN string must started with `n`'",
Expand Down Expand Up @@ -249,6 +252,7 @@ impl Error {
Self::InvalidConsecutiveFractionDigitSeparator => None,
Self::InvalidConsecutiveExponentDigitSeparator => None,
Self::InvalidFlags => None,
Self::Unsupported => None,

// OPTION ERRORS
Self::InvalidNanString => None,
Expand Down Expand Up @@ -314,6 +318,7 @@ impl Error {
InvalidConsecutiveExponentDigitSeparator
);
is_error_type!(is_invalid_flags, InvalidFlags);
is_error_type!(is_unsupported, Unsupported);
is_error_type!(is_invalid_nan_string, InvalidNanString);
is_error_type!(is_nan_string_too_long, NanStringTooLong);
is_error_type!(is_invalid_inf_string, InvalidInfString);
Expand Down Expand Up @@ -411,6 +416,7 @@ impl fmt::Display for Error {
format_message!(formatter, description)
},
Self::InvalidFlags => format_message!(formatter, description),
Self::Unsupported => format_message!(formatter, description),

// OPTION ERRORS
Self::InvalidNanString => options_message!(formatter, description),
Expand Down
72 changes: 72 additions & 0 deletions lexical-util/src/feature_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,78 @@ impl<const FORMAT: u128> NumberFormat<FORMAT> {
Self::REQUIRED_FRACTION_DIGITS_WITH_EXPONENT
}

/// If the format supports parsing integers.
///
/// See [`supports_parsing_integers`][Self::supports_parsing_integers].
pub const SUPPORTS_PARSING_INTEGERS: bool = from_flag!(FORMAT, SUPPORTS_PARSING_INTEGERS);

/// Get if the format supports parsing integers.
///
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
/// to [`true`].
///
/// # Used For
///
/// - Parse Integer
#[inline(always)]
pub const fn supports_parsing_integers(&self) -> bool {
Self::SUPPORTS_PARSING_INTEGERS
}

/// If the format supports parsing floats.
///
/// See [`supports_parsing_floats`][Self::supports_parsing_floats].
pub const SUPPORTS_PARSING_FLOATS: bool = from_flag!(FORMAT, SUPPORTS_PARSING_FLOATS);

/// Get if the format supports parsing floats.
///
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
/// to [`true`].
///
/// # Used For
///
/// - Parse Float
#[inline(always)]
pub const fn supports_parsing_floats(&self) -> bool {
Self::SUPPORTS_PARSING_FLOATS
}

/// If the format supports writing integers.
///
/// See [`supports_writing_integers`][Self::supports_writing_integers].
pub const SUPPORTS_WRITING_INTEGERS: bool = from_flag!(FORMAT, SUPPORTS_WRITING_INTEGERS);

/// Get if the format supports writing integers.
///
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
/// to [`true`].
///
/// # Used For
///
/// - Write Integer
#[inline(always)]
pub const fn supports_writing_integers(&self) -> bool {
Self::SUPPORTS_WRITING_INTEGERS
}

/// If the format supports writing floats.
///
/// See [`supports_writing_floats`][Self::supports_writing_floats].
pub const SUPPORTS_WRITING_FLOATS: bool = from_flag!(FORMAT, SUPPORTS_WRITING_FLOATS);

/// Get if the format supports writing floats.
///
/// Can only be modified with [`feature`][crate#features] `format`. Defaults
/// to [`true`].
///
/// # Used For
///
/// - Write Float
#[inline(always)]
pub const fn supports_writing_floats(&self) -> bool {
Self::SUPPORTS_WRITING_FLOATS
}

// DIGIT SEPARATOR FLAGS & MASKS

/// If digit separators are allowed between integer digits.
Expand Down
Loading

0 comments on commit f53fae0

Please sign in to comment.