use core::fmt;
use core::str::FromStr;
use crate::Decimal;
use crate::FixedInteger;
use crate::ParseError;
#[derive(Debug, Clone, PartialEq)]
pub struct ScientificDecimal {
significand: Decimal,
exponent: FixedInteger,
}
impl ScientificDecimal {
pub fn from(significand: Decimal, exponent: FixedInteger) -> Self {
ScientificDecimal {
significand,
exponent,
}
}
}
impl writeable::Writeable for ScientificDecimal {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
self.significand.write_to(sink)?;
sink.write_char('e')?;
self.exponent.write_to(sink)
}
fn writeable_length_hint(&self) -> writeable::LengthHint {
self.significand.writeable_length_hint() + 1 + self.exponent.writeable_length_hint()
}
}
writeable::impl_display_with_writeable!(ScientificDecimal);
impl ScientificDecimal {
#[inline]
pub fn try_from_str(s: &str) -> Result<Self, ParseError> {
Self::try_from_utf8(s.as_bytes())
}
pub fn try_from_utf8(code_units: &[u8]) -> Result<Self, ParseError> {
if code_units.contains(&b'E') {
return Err(ParseError::Syntax);
}
let mut parts = code_units.split(|&c| c == b'e');
let significand = parts.next().ok_or(ParseError::Syntax)?;
let exponent = parts.next().ok_or(ParseError::Syntax)?;
if parts.next().is_some() {
return Err(ParseError::Syntax);
}
Ok(ScientificDecimal::from(
Decimal::try_from_utf8(significand)?,
FixedInteger::try_from_utf8(exponent)?,
))
}
}
impl FromStr for ScientificDecimal {
type Err = ParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
#[test]
fn test_scientific_syntax_error() {
#[derive(Debug)]
struct TestCase {
pub input_str: &'static str,
pub expected_err: Option<ParseError>,
}
let cases = [
TestCase {
input_str: "5",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-123c4",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-123e",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "1e10",
expected_err: None,
},
TestCase {
input_str: "1e1e1",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "1e1E1",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "1E1e1",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-1e+01",
expected_err: None,
},
TestCase {
input_str: "-1e+1.0",
expected_err: Some(ParseError::Limit),
},
TestCase {
input_str: "-1e+-1",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "123E4",
expected_err: Some(ParseError::Syntax),
},
];
for cas in &cases {
match ScientificDecimal::from_str(cas.input_str) {
Ok(dec) => {
assert_eq!(cas.expected_err, None, "{cas:?}");
assert_eq!(cas.input_str, dec.to_string(), "{cas:?}");
}
Err(err) => {
assert_eq!(cas.expected_err, Some(err), "{cas:?}");
}
}
}
}