refactor: enhance `parseFixed` logic with `tryParseFixed` support and improve error handling in edge cases, including unit tests (#2682)
Konstantin Ullrich committed
Nov 26, 2025 at 14:55 UTC
6b725cceeb281063949e375a06d9b4e48b4615a2
2 files changed
+90
-3
cw_core/lib/parse_fixed.dart
+31
-3
@@ -1,23 +1,51 @@
1
+/// Parses the string [value] as a fixed-point decimal literal and returns its
2
+/// [BigInt] value.
3
+///
4
+/// The number of fractional digits is determined by [decimals].
5
+///
6
+/// Returns `null` if the input [value] is not a valid fixed-point literal
7
+/// (e.g., non-numeric characters, too many fractional digits).
8
+///
9
+/// Like [parseFixed], except that this function returns `null` for invalid inputs
10
+/// instead of throwing.
11
+BigInt? tryParseFixed(String value, int decimals) {
12
+ try {
13
+ return parseFixed(value, decimals);
14
+ } on FormatException catch (_) {
15
+ return null;
16
+ }
17
+}
18
+
19
+/// Parses the string [value] as a fixed-point decimal literal and returns its
20
+/// [BigInt] value.
21
+///
22
+/// The number of fractional digits is determined by [decimals].
23
+///
24
+/// Throws a [FormatException] if the input [value] is not a valid fixed-point literal
25
+/// (e.g., non-numeric characters, too many fractional digits).
26
+///
27
+/// Rather than throwing and immediately catching the [FormatException],
28
+/// instead use [tryParseFixed] to handle a potential parsing error.
29
BigInt parseFixed(String value, int decimals) {
30
final multiplier = getMultiplier(decimals);
31
32
final negative = value.startsWith("-");
33
if (negative) value = value.substring(1);
34
7
- if (value == ".") throw Exception("missing value, value, $value");
35
+ if (value == ".") throw FormatException("missing value, value, $value");
36
37
if (value.startsWith(".")) value = "0$value";
38
39
final comps = value.split(".");
40
if (comps.length > 2) {
13
- throw Exception("too many decimal points, value, $value");
41
+ throw FormatException("too many decimal points, value, $value");
42
}
43
44
var whole = comps.isNotEmpty ? comps[0] : "0";
45
var fraction = (comps.length == 2 ? comps[1] : "0").padRight(decimals, "0");
46
47
if (fraction.length > multiplier.length - 1) {
20
- throw Exception(
48
+ throw FormatException(
49
"fractional component exceeds decimals, underflow, parseFixed");
50
}
51
cw_core/test/parse_fixed_test.dart
+59
@@ -34,5 +34,64 @@ void main() {
34
35
test('should parse -.00002 as -20', () => expect(parseFixed("-.00002", 6), BigInt.from(-20)));
36
});
37
+
38
+ group('parseFixed, failing', () {
39
+ test('should fail to parse .000.0010, too many fractional digits',
40
+ () => expect(() => parseFixed(".000.0010", 6), throwsFormatException));
41
+
42
+ test('should fail to parse .0000010, fractional component exceeds decimals',
43
+ () => expect(() => parseFixed(".0000010", 6), throwsFormatException));
44
+
45
+ test('should fail to parse `.`, missing value',
46
+ () => expect(() => parseFixed(".", 6), throwsFormatException));
47
+ });
48
+ });
49
+
50
+ group('tryParseFixed', () {
51
+ group('tryParseFixed, positive', () {
52
+ test('should parse 1.000001 as 1000001',
53
+ () => expect(tryParseFixed("1.000001", 6), BigInt.from(1000001)));
54
+
55
+ test('should parse 1 as 1000000', () => expect(tryParseFixed("1", 6), BigInt.from(1000000)));
56
+
57
+ test(
58
+ 'should parse 1. as 1000000', () => expect(tryParseFixed("1.", 6), BigInt.from(1000000)));
59
+
60
+ test('should parse 1.1 as 1100000',
61
+ () => expect(tryParseFixed("1.1", 6), BigInt.from(1100000)));
62
+
63
+ test('should parse 01.1 as 1100000',
64
+ () => expect(tryParseFixed("01.1", 6), BigInt.from(1100000)));
65
+
66
+ test('should parse 1100000 as 11000000',
67
+ () => expect(tryParseFixed("1100000", 1), BigInt.from(11000000)));
68
+ });
69
+
70
+ group('tryParseFixed, negative', () {
71
+ test('should parse -1.000001 as -1000001',
72
+ () => expect(tryParseFixed("-1.000001", 6), BigInt.from(-1000001)));
73
+
74
+ test('should parse -1 as 1000000',
75
+ () => expect(tryParseFixed("-1", 6), BigInt.from(-1000000)));
76
+ });
77
+
78
+ group('tryParseFixed, no leading 0', () {
79
+ test('should parse .000001 as 1', () => expect(tryParseFixed(".000001", 6), BigInt.from(1)));
80
+
81
+ test('should parse .00002 as 20', () => expect(tryParseFixed(".00002", 6), BigInt.from(20)));
82
+
83
+ test('should parse -.00002 as -20',
84
+ () => expect(tryParseFixed("-.00002", 6), BigInt.from(-20)));
85
+ });
86
+
87
+ group('tryParseFixed, return `null`', () {
88
+ test('should parse .000.0010 as null, too many fractional digits',
89
+ () => expect(tryParseFixed(".000.0010", 6), isNull));
90
+
91
+ test('should parse .0000010 as null, fractional component exceeds decimals',
92
+ () => expect(tryParseFixed(".0000010", 6), isNull));
93
+
94
+ test('should parse . as `null`, missing value', () => expect(tryParseFixed(".", 6), isNull));
95
+ });
96
});
97
}