fixed_decimal/
scientific.rs1use core::fmt;
6use core::str::FromStr;
7
8use crate::Decimal;
9use crate::FixedInteger;
10use crate::ParseError;
11
12#[derive(Debug, Clone, PartialEq)]
18pub struct ScientificDecimal {
19 significand: Decimal,
20 exponent: FixedInteger,
21}
22
23impl ScientificDecimal {
24 pub fn from(significand: Decimal, exponent: FixedInteger) -> Self {
25 ScientificDecimal {
26 significand,
27 exponent,
28 }
29 }
30}
31
32impl writeable::Writeable for ScientificDecimal {
64 fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
65 self.significand.write_to(sink)?;
66 sink.write_char('e')?;
67 self.exponent.write_to(sink)
68 }
69
70 fn writeable_length_hint(&self) -> writeable::LengthHint {
71 self.significand.writeable_length_hint() + 1 + self.exponent.writeable_length_hint()
72 }
73}
74
75writeable::impl_display_with_writeable!(ScientificDecimal);
76
77impl ScientificDecimal {
78 #[inline]
79 pub fn try_from_str(s: &str) -> Result<Self, ParseError> {
80 Self::try_from_utf8(s.as_bytes())
81 }
82
83 pub fn try_from_utf8(code_units: &[u8]) -> Result<Self, ParseError> {
84 if code_units.contains(&b'E') {
88 return Err(ParseError::Syntax);
89 }
90 let mut parts = code_units.split(|&c| c == b'e');
91 let significand = parts.next().ok_or(ParseError::Syntax)?;
92 let exponent = parts.next().ok_or(ParseError::Syntax)?;
93 if parts.next().is_some() {
94 return Err(ParseError::Syntax);
95 }
96 Ok(ScientificDecimal::from(
97 Decimal::try_from_utf8(significand)?,
98 FixedInteger::try_from_utf8(exponent)?,
99 ))
100 }
101}
102
103impl FromStr for ScientificDecimal {
104 type Err = ParseError;
105 #[inline]
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 Self::try_from_str(s)
108 }
109}
110
111#[test]
112fn test_scientific_syntax_error() {
113 #[derive(Debug)]
114 struct TestCase {
115 pub input_str: &'static str,
116 pub expected_err: Option<ParseError>,
117 }
118 let cases = [
119 TestCase {
120 input_str: "5",
121 expected_err: Some(ParseError::Syntax),
122 },
123 TestCase {
124 input_str: "-123c4",
125 expected_err: Some(ParseError::Syntax),
126 },
127 TestCase {
128 input_str: "-123e",
129 expected_err: Some(ParseError::Syntax),
130 },
131 TestCase {
132 input_str: "1e10",
133 expected_err: None,
134 },
135 TestCase {
136 input_str: "1e1e1",
137 expected_err: Some(ParseError::Syntax),
138 },
139 TestCase {
140 input_str: "1e1E1",
141 expected_err: Some(ParseError::Syntax),
142 },
143 TestCase {
144 input_str: "1E1e1",
145 expected_err: Some(ParseError::Syntax),
146 },
147 TestCase {
148 input_str: "-1e+01",
149 expected_err: None,
150 },
151 TestCase {
152 input_str: "-1e+1.0",
153 expected_err: Some(ParseError::Limit),
154 },
155 TestCase {
156 input_str: "-1e+-1",
157 expected_err: Some(ParseError::Syntax),
158 },
159 TestCase {
160 input_str: "123E4",
161 expected_err: Some(ParseError::Syntax),
162 },
163 ];
164 for cas in &cases {
165 match ScientificDecimal::from_str(cas.input_str) {
166 Ok(dec) => {
167 assert_eq!(cas.expected_err, None, "{cas:?}");
168 assert_eq!(cas.input_str, dec.to_string(), "{cas:?}");
169 }
170 Err(err) => {
171 assert_eq!(cas.expected_err, Some(err), "{cas:?}");
172 }
173 }
174 }
175}