#![doc = include_str!("../README.md")]
#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::borrow::ToOwned;
#[derive(PartialEq, Eq, Debug)]
pub struct ISO6093Error;
#[derive(Debug, PartialEq)]
pub enum ISO6093RealNumber {
NR1(f64),
NR2(f64),
NR3(f64),
}
pub fn parse_nr1(mut input: &str) -> Result<f64, ISO6093Error> {
input = input.trim_start_matches(|c| c == ' ');
input
.parse::<i64>()
.map(|v| v as f64)
.map_err(|_| ISO6093Error)
}
fn parse_nr2_ex(mut input: &str) -> Result<f64, ISO6093Error> {
input = input.trim_start_matches(|c| c == ' ');
if input.chars().next().is_some_and(|c| c == '.') {
return Err(ISO6093Error);
}
let mut chars = input.chars();
if chars.next().is_some_and(|c| c == '+' || c == '-') {
if chars.next().is_some_and(|c| c == '.') {
return Err(ISO6093Error);
}
}
let mut has_decimal = false;
let mut maybe_comma_index: Option<usize> = None;
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == ',' {
if has_decimal {
return Err(ISO6093Error);
}
has_decimal = true;
maybe_comma_index = Some(i);
} else if c == '.' {
if has_decimal {
return Err(ISO6093Error);
}
has_decimal = true;
} else if c.to_ascii_lowercase() == 'e' {
return Err(ISO6093Error);
}
}
if !has_decimal {
return Err(ISO6093Error);
}
if let Some(comma_index) = maybe_comma_index {
#[cfg(feature = "alloc")]
{
let mut normalized = input.to_owned();
unsafe {
normalized.as_bytes_mut()[comma_index] = b'.';
}
return normalized.parse::<f64>().map_err(|_| ISO6093Error);
}
#[cfg(not(feature = "alloc"))]
{
unreachable!();
}
} else {
return input.parse::<f64>().map_err(|_| ISO6093Error);
}
}
fn parse_nr3_ex(mut input: &str) -> Result<f64, ISO6093Error> {
input = input.trim_start_matches(|c| c == ' ');
let mut has_exponent = false;
let mut maybe_comma_index: Option<usize> = None;
let mut has_decimal = false;
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == '.' || c == ',' {
if has_decimal {
return Err(ISO6093Error);
}
if has_exponent {
return Err(ISO6093Error);
}
has_decimal = true;
if c == ',' {
maybe_comma_index = Some(i);
}
} else if c == 'E' || c == 'e' {
has_exponent = true;
}
}
if !has_exponent || !has_decimal {
return Err(ISO6093Error);
}
if let Some(comma_index) = maybe_comma_index {
#[cfg(feature = "alloc")]
{
let mut normalized = input.to_owned();
unsafe {
normalized.as_bytes_mut()[comma_index] = b'.';
}
return normalized.parse::<f64>().map_err(|_| ISO6093Error);
}
#[cfg(not(feature = "alloc"))]
{
unreachable!();
}
} else {
return input.parse::<f64>().map_err(|_| ISO6093Error);
}
}
fn parse_iso6093_ex(mut input: &str) -> Result<ISO6093RealNumber, ISO6093Error> {
input = input.trim_start_matches(|c| c == ' ');
if input.chars().next().is_some_and(|c| c == '.') {
return Err(ISO6093Error);
}
let mut chars = input.chars();
if chars.next().is_some_and(|c| c == '+' || c == '-') {
if chars.next().is_some_and(|c| c == '.') {
return Err(ISO6093Error);
}
}
let mut has_exponent = false;
let mut has_decimal = false;
let mut maybe_comma_index: Option<usize> = None;
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == '.' || c == ',' {
if has_decimal {
return Err(ISO6093Error);
}
if has_exponent {
return Err(ISO6093Error);
}
has_decimal = true;
if c == ',' {
maybe_comma_index = Some(i);
}
} else if c == 'E' || c == 'e' {
has_exponent = true;
}
}
if !has_decimal {
return parse_nr1(input).map(|v| ISO6093RealNumber::NR1(v));
}
if let Some(comma_index) = maybe_comma_index {
#[cfg(feature = "alloc")]
{
let mut normalized = input.to_owned();
unsafe {
normalized.as_bytes_mut()[comma_index] = b'.';
}
return normalized
.parse::<f64>()
.map(|v| {
if has_exponent {
ISO6093RealNumber::NR3(v)
} else {
ISO6093RealNumber::NR2(v)
}
})
.map_err(|_| ISO6093Error);
}
#[cfg(not(feature = "alloc"))]
{
unreachable!();
}
} else {
return input
.parse::<f64>()
.map(|v| {
if has_exponent {
ISO6093RealNumber::NR3(v)
} else {
ISO6093RealNumber::NR2(v)
}
})
.map_err(|_| ISO6093Error);
}
}
#[cfg(feature = "alloc")]
#[inline(always)]
pub fn parse_nr2(input: &str) -> Result<f64, ISO6093Error> {
parse_nr2_ex(input)
}
#[cfg(feature = "alloc")]
#[inline(always)]
pub fn parse_nr3(input: &str) -> Result<f64, ISO6093Error> {
parse_nr3_ex(input)
}
#[cfg(feature = "alloc")]
#[inline(always)]
pub fn parse_iso6093(input: &str) -> Result<ISO6093RealNumber, ISO6093Error> {
parse_iso6093_ex(input)
}
#[cfg(not(feature = "alloc"))]
pub fn parse_nr2(input: &mut str) -> Result<f64, ISO6093Error> {
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == ',' {
unsafe {
input.as_bytes_mut()[i] = b'.';
}
break;
}
}
parse_nr2_ex(input)
}
#[cfg(not(feature = "alloc"))]
pub fn parse_nr3(input: &mut str) -> Result<f64, ISO6093Error> {
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == ',' {
unsafe {
input.as_bytes_mut()[i] = b'.';
}
break;
}
}
parse_nr3_ex(input)
}
#[cfg(not(feature = "alloc"))]
pub fn parse_iso6093(input: &mut str) -> Result<ISO6093RealNumber, ISO6093Error> {
for (i, c) in input.char_indices() {
if !c.is_ascii() {
return Err(ISO6093Error);
}
if c == ',' {
unsafe {
input.as_bytes_mut()[i] = b'.';
}
break;
}
}
parse_iso6093_ex(input)
}
#[inline]
pub fn fmt_nr1(num: f64, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let inum: i64 = num as i64;
write!(f, "{}", inum)
}
pub unsafe fn fmt_nr2(num: f64, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if num.is_finite() && (num == (num as i64 as f64)) {
let inum: i64 = num as i64;
return write!(f, "{}.", inum); }
write!(f, "{}", num)
}
#[inline]
pub fn fmt_nr3(num: f64, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:e}", num)
}
impl core::fmt::Display for ISO6093RealNumber {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ISO6093RealNumber::NR1(v) => fmt_nr1(*v, f),
ISO6093RealNumber::NR2(v) => unsafe { fmt_nr2(*v, f) },
ISO6093RealNumber::NR3(v) => fmt_nr3(*v, f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate alloc;
use alloc::string::ToString;
#[cfg(feature = "alloc")]
macro_rules! s {
($arg:expr) => {
$arg
};
}
#[cfg(not(feature = "alloc"))]
macro_rules! s {
($arg:expr) => {
alloc::string::ToString::to_string($arg).as_mut_str()
};
}
#[test]
fn test_parse_nr1() {
assert_eq!(parse_nr1(s!("0004902")), Ok(4902.0));
assert_eq!(parse_nr1(s!(" 4902")), Ok(4902.0));
assert_eq!(parse_nr1(s!(" 4902")), Ok(4902.0));
assert_eq!(parse_nr1(s!("0001234")), Ok(1234.0));
assert_eq!(parse_nr1(s!(" 1234")), Ok(1234.0));
assert_eq!(parse_nr1(s!("0000000")), Ok(0.0));
assert_eq!(parse_nr1(s!(" 0")), Ok(0.0));
assert_eq!(parse_nr1(s!("1234567")), Ok(1234567.0));
assert_eq!(parse_nr1(s!("+004902")), Ok(4902.0));
assert_eq!(parse_nr1(s!(" +4902")), Ok(4902.0));
assert_eq!(parse_nr1(s!(" +4902")), Ok(4902.0));
assert_eq!(parse_nr1(s!(" 4902")), Ok(4902.0));
assert_eq!(parse_nr1(s!("+001234")), Ok(1234.0));
assert_eq!(parse_nr1(s!(" +1234")), Ok(1234.0));
assert_eq!(parse_nr1(s!(" 1234")), Ok(1234.0));
assert_eq!(parse_nr1(s!("-56780")), Ok(-56780.0));
assert_eq!(parse_nr1(s!(" -56780")), Ok(-56780.0));
assert_eq!(parse_nr1(s!("+000000")), Ok(0.0));
assert_eq!(parse_nr1(s!(" +0")), Ok(0.0));
assert_eq!(parse_nr1(s!(" 0")), Ok(0.0));
assert_eq!(parse_nr1(s!("123")), Ok(123.0));
assert_eq!(parse_nr1(s!("-456")), Ok(-456.0));
assert_eq!(parse_nr1(s!("+789")), Ok(789.0));
assert_eq!(parse_nr1(s!("")), Err(ISO6093Error));
assert_eq!(parse_nr1(s!("12.3")), Err(ISO6093Error));
assert_eq!(parse_nr1(s!("12E3")), Err(ISO6093Error));
}
#[test]
fn test_parse_nr2() {
assert_eq!(parse_nr2(s!("1327.000")), Ok(1327.0));
assert_eq!(parse_nr2(s!("0001327.")), Ok(1327.0));
assert_eq!(parse_nr2(s!(" 1327.")), Ok(1327.0));
assert_eq!(parse_nr2(s!("00123.45")), Ok(123.45));
assert_eq!(parse_nr2(s!(" 123.45")), Ok(123.45));
assert_eq!(parse_nr2(s!(" 1237.0")), Ok(1237.0));
assert_eq!(parse_nr2(s!("00.00001")), Ok(0.00001));
assert_eq!(parse_nr2(s!("1234,567")), Ok(1234.567));
assert_eq!(parse_nr2(s!("000,0000")), Ok(0.0));
assert_eq!(parse_nr2(s!(" 0,0")), Ok(0.0));
assert_eq!(parse_nr2(s!("+1327.00")), Ok(1327.00));
assert_eq!(parse_nr2(s!(" +1327.")), Ok(1327.0));
assert_eq!(parse_nr2(s!(" 1327.")), Ok(1327.0));
assert_eq!(parse_nr2(s!(" +123.45")), Ok(123.45));
assert_eq!(parse_nr2(s!(" 123,45")), Ok(123.45));
assert_eq!(parse_nr2(s!(" +1237.0")), Ok(1237.0));
assert_eq!(parse_nr2(s!(" 1237,0")), Ok(1237.0));
assert_eq!(parse_nr2(s!("+0.00001")), Ok(0.00001));
assert_eq!(parse_nr2(s!("-5,67800")), Ok(-5.67800));
assert_eq!(parse_nr2(s!("-05,6780")), Ok(-5.6780));
assert_eq!(parse_nr2(s!("+0.00000")), Ok(0.0));
assert_eq!(parse_nr2(s!(" +0,0")), Ok(0.0));
assert_eq!(parse_nr2(s!(" 0,0")), Ok(0.0));
assert_eq!(parse_nr2(s!(" 0,")), Ok(0.0));
assert_eq!(parse_nr2(s!("123.45")), Ok(123.45));
assert_eq!(parse_nr2(s!("-67.89")), Ok(-67.89));
assert_eq!(parse_nr2(s!("+0.123")), Ok(0.123));
assert_eq!(parse_nr2(s!("")), Err(ISO6093Error));
assert_eq!(parse_nr2(s!("123")), Err(ISO6093Error));
assert_eq!(parse_nr2(s!("12E3")), Err(ISO6093Error));
}
#[test]
fn test_parse_nr3() {
assert_eq!(parse_nr3(s!("+0,56E+4")), Ok(5600.0));
assert_eq!(parse_nr3(s!("+5.6e+03")), Ok(5600.0));
assert_eq!(parse_nr3(s!("+0,3E-04")), Ok(0.00003));
assert_eq!(parse_nr3(s!(" 0,3e-04")), Ok(0.00003));
assert_eq!(parse_nr3(s!("-2,8E+00")), Ok(-2.8));
assert_eq!(parse_nr3(s!("+0,0E+00")), Ok(0.0));
assert_eq!(parse_nr3(s!(" 0.e+0")), Ok(0.0));
assert_eq!(parse_nr3(s!("1.23E+45")), Ok(1.23E+45));
assert_eq!(parse_nr3(s!("-6.78e-9")), Ok(-6.78e-9));
assert_eq!(parse_nr3(s!("+1.0E2")), Ok(100.0));
assert_eq!(parse_nr3(s!("")), Err(ISO6093Error));
assert_eq!(parse_nr3(s!("123.45")), Err(ISO6093Error));
assert_eq!(parse_nr3(s!("123")), Err(ISO6093Error));
}
#[test]
fn test_parse_iso6093() {
assert_eq!(
parse_iso6093(s!("0004902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!(" 4902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!(" 4902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!("0001234")),
Ok(ISO6093RealNumber::NR1(1234.0))
);
assert_eq!(
parse_iso6093(s!(" 1234")),
Ok(ISO6093RealNumber::NR1(1234.0))
);
assert_eq!(
parse_iso6093(s!("0000000")),
Ok(ISO6093RealNumber::NR1(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0")),
Ok(ISO6093RealNumber::NR1(0.0))
);
assert_eq!(
parse_iso6093(s!("1234567")),
Ok(ISO6093RealNumber::NR1(1234567.0))
);
assert_eq!(
parse_iso6093(s!("+004902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!(" +4902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!(" +4902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!(" 4902")),
Ok(ISO6093RealNumber::NR1(4902.0))
);
assert_eq!(
parse_iso6093(s!("+001234")),
Ok(ISO6093RealNumber::NR1(1234.0))
);
assert_eq!(
parse_iso6093(s!(" +1234")),
Ok(ISO6093RealNumber::NR1(1234.0))
);
assert_eq!(
parse_iso6093(s!(" 1234")),
Ok(ISO6093RealNumber::NR1(1234.0))
);
assert_eq!(
parse_iso6093(s!("-56780")),
Ok(ISO6093RealNumber::NR1(-56780.0))
);
assert_eq!(
parse_iso6093(s!(" -56780")),
Ok(ISO6093RealNumber::NR1(-56780.0))
);
assert_eq!(
parse_iso6093(s!("+000000")),
Ok(ISO6093RealNumber::NR1(0.0))
);
assert_eq!(
parse_iso6093(s!(" +0")),
Ok(ISO6093RealNumber::NR1(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0")),
Ok(ISO6093RealNumber::NR1(0.0))
);
assert_eq!(
parse_iso6093(s!("1327.000")),
Ok(ISO6093RealNumber::NR2(1327.0))
);
assert_eq!(
parse_iso6093(s!("0001327.")),
Ok(ISO6093RealNumber::NR2(1327.0))
);
assert_eq!(
parse_iso6093(s!(" 1327.")),
Ok(ISO6093RealNumber::NR2(1327.0))
);
assert_eq!(
parse_iso6093(s!("00123.45")),
Ok(ISO6093RealNumber::NR2(123.45))
);
assert_eq!(
parse_iso6093(s!(" 123.45")),
Ok(ISO6093RealNumber::NR2(123.45))
);
assert_eq!(
parse_iso6093(s!(" 1237.0")),
Ok(ISO6093RealNumber::NR2(1237.0))
);
assert_eq!(
parse_iso6093(s!("00.00001")),
Ok(ISO6093RealNumber::NR2(0.00001))
);
assert_eq!(
parse_iso6093(s!("1234,567")),
Ok(ISO6093RealNumber::NR2(1234.567))
);
assert_eq!(
parse_iso6093(s!("000,0000")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0,0")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!("+1327.00")),
Ok(ISO6093RealNumber::NR2(1327.00))
);
assert_eq!(
parse_iso6093(s!(" +1327.")),
Ok(ISO6093RealNumber::NR2(1327.0))
);
assert_eq!(
parse_iso6093(s!(" 1327.")),
Ok(ISO6093RealNumber::NR2(1327.0))
);
assert_eq!(
parse_iso6093(s!(" +123.45")),
Ok(ISO6093RealNumber::NR2(123.45))
);
assert_eq!(
parse_iso6093(s!(" 123,45")),
Ok(ISO6093RealNumber::NR2(123.45))
);
assert_eq!(
parse_iso6093(s!(" +1237.0")),
Ok(ISO6093RealNumber::NR2(1237.0))
);
assert_eq!(
parse_iso6093(s!(" 1237,0")),
Ok(ISO6093RealNumber::NR2(1237.0))
);
assert_eq!(
parse_iso6093(s!("+0.00001")),
Ok(ISO6093RealNumber::NR2(0.00001))
);
assert_eq!(
parse_iso6093(s!("-5,67800")),
Ok(ISO6093RealNumber::NR2(-5.67800))
);
assert_eq!(
parse_iso6093(s!("-05,6780")),
Ok(ISO6093RealNumber::NR2(-5.6780))
);
assert_eq!(
parse_iso6093(s!("+0.00000")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!(" +0,0")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0,0")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0,")),
Ok(ISO6093RealNumber::NR2(0.0))
);
assert_eq!(
parse_iso6093(s!("+0,56E+4")),
Ok(ISO6093RealNumber::NR3(5600.0))
);
assert_eq!(
parse_iso6093(s!("+5.6e+03")),
Ok(ISO6093RealNumber::NR3(5600.0))
);
assert_eq!(
parse_iso6093(s!("+0,3E-04")),
Ok(ISO6093RealNumber::NR3(0.00003))
);
assert_eq!(
parse_iso6093(s!(" 0,3e-04")),
Ok(ISO6093RealNumber::NR3(0.00003))
);
assert_eq!(
parse_iso6093(s!("-2,8E+00")),
Ok(ISO6093RealNumber::NR3(-2.8))
);
assert_eq!(
parse_iso6093(s!("+0,0E+00")),
Ok(ISO6093RealNumber::NR3(0.0))
);
assert_eq!(
parse_iso6093(s!(" 0.e+0")),
Ok(ISO6093RealNumber::NR3(0.0))
);
}
#[test]
fn test_print_nr1() {
assert_eq!(ISO6093RealNumber::NR1(123.0).to_string().as_str(), "123");
assert_eq!(ISO6093RealNumber::NR1(-123.0).to_string().as_str(), "-123");
assert_eq!(ISO6093RealNumber::NR1(123.5).to_string().as_str(), "123");
}
#[test]
fn test_print_nr2() {
assert_eq!(ISO6093RealNumber::NR2(123.0).to_string().as_str(), "123.");
assert_eq!(ISO6093RealNumber::NR2(-123.0).to_string().as_str(), "-123.");
assert_eq!(ISO6093RealNumber::NR2(123.5).to_string().as_str(), "123.5");
assert_eq!(
ISO6093RealNumber::NR2(0.00123).to_string().as_str(),
"0.00123"
);
}
#[test]
fn test_print_nr3() {
assert_eq!(
ISO6093RealNumber::NR3(12300.0).to_string().as_str(),
"1.23e4"
);
assert_eq!(
ISO6093RealNumber::NR3(-12300.0).to_string().as_str(),
"-1.23e4"
);
assert_eq!(
ISO6093RealNumber::NR3(12300.5).to_string().as_str(),
"1.23005e4"
);
assert_eq!(
ISO6093RealNumber::NR3(0.00123).to_string().as_str(),
"1.23e-3"
);
}
}