From 8b176349fd774cf7daec773114cc407199e84fa1 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 27 Nov 2025 07:12:45 +0000 Subject: [PATCH 1/3] feat: add xsd:decimal codec --- CHANGELOG.md | 3 +- README.md | 2 +- lib/src/codecs/codecs.dart | 1 + lib/src/codecs/decimal/decimal_codec.dart | 41 +++++++++++++++++++++++ test/codecs/decimal_codec_test.dart | 30 +++++++++++++++++ 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 lib/src/codecs/decimal/decimal_codec.dart create mode 100644 test/codecs/decimal_codec_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd4195..acd4018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,4 +27,5 @@ - Implement `xsd:gMonthDay` codec and data type - Implement `xsd:duration` codec and data type - Implement `xsd:datetime` codec and data type -- Implement `xsd:anyUri` codec \ No newline at end of file +- Implement `xsd:anyUri` codec +- Implement `xsd:decimal` codec \ No newline at end of file diff --git a/README.md b/README.md index 71a2bc4..9965b50 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ This library aims to support the following XSD 1.1 built-in datatypes that are c | `xsd:Name` | ✅ | ✅ | `String` | `dart:core` | | `xsd:language` | ✅ | ✅ | `Locale` | `package:intl` | | `xsd:NCName` | ✅ | ✅ | `String` | `dart:core` | -| `xsd:decimal` | ✅ | ❌ | `Decimal` | `package:decimal` | +| `xsd:decimal` | ✅ | ✅ | `Decimal` | `package:decimal` | | `xsd:double` | ✅ | ❌ | `double` | `dart:core` | | `xsd:float` | ✅ | ❌ | `double` | `dart:core` | | `xsd:integer` | ✅ | ❌ | `BigInt` | `dart:core` | diff --git a/lib/src/codecs/codecs.dart b/lib/src/codecs/codecs.dart index ac8d63f..7e65e13 100644 --- a/lib/src/codecs/codecs.dart +++ b/lib/src/codecs/codecs.dart @@ -25,3 +25,4 @@ export 'g_month_day/gregorian_month_day_codec.dart'; export 'duration/xsd_duration_codec.dart'; export 'datetime/xsd_datetime_codec.dart'; export 'any_uri/any_uri_codec.dart'; +export 'decimal/decimal_codec.dart'; \ No newline at end of file diff --git a/lib/src/codecs/decimal/decimal_codec.dart b/lib/src/codecs/decimal/decimal_codec.dart new file mode 100644 index 0000000..79d8ed7 --- /dev/null +++ b/lib/src/codecs/decimal/decimal_codec.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:decimal/decimal.dart'; +import 'package:xsd/src/helpers/whitespace.dart'; + +/// A [Codec] that converts between XSD `decimal` strings and [Decimal] objects. +class XsdDecimalCodec extends Codec { + const XsdDecimalCodec(); + + @override + Converter get encoder => const XsdDecimalEncoder(); + + @override + Converter get decoder => const XsdDecimalDecoder(); +} + +class XsdDecimalEncoder extends Converter { + const XsdDecimalEncoder(); + + @override + String convert(Decimal input) => input.toString(); +} + +class XsdDecimalDecoder extends Converter { + const XsdDecimalDecoder(); + + static final _validationRegex = RegExp( + r'^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)$', + ); + + @override + Decimal convert(String input) { + final trimmed = processWhiteSpace(input, Whitespace.collapse); + + if (!_validationRegex.hasMatch(trimmed)) { + throw FormatException('Invalid XSD decimal format', input); + } + + return Decimal.parse(trimmed); + } +} diff --git a/test/codecs/decimal_codec_test.dart b/test/codecs/decimal_codec_test.dart new file mode 100644 index 0000000..32e1d1d --- /dev/null +++ b/test/codecs/decimal_codec_test.dart @@ -0,0 +1,30 @@ +import 'package:decimal/decimal.dart'; +import 'package:test/test.dart'; +import 'package:xsd/xsd.dart'; + +void main() { + group('XsdDecimalCodec', () { + const codec = XsdDecimalCodec(); + + test('should encode valid decimal', () { + expect(codec.encode(Decimal.parse('123.45')), '123.45'); + expect(codec.encode(Decimal.parse('-123.45')), '-123.45'); + }); + + test('should decode valid decimal', () { + expect(codec.decode('123.45'), Decimal.parse('123.45')); + expect(codec.decode('-123.45'), Decimal.parse('-123.45')); + expect(codec.decode(' +123.45 '), Decimal.parse('123.45')); + expect(codec.decode('.45'), Decimal.parse('0.45')); + }); + + test('should throw on invalid decimal', () { + expect(() => codec.decode('12.3.4'), throwsFormatException); + expect(() => codec.decode('abc'), throwsFormatException); + expect( + () => codec.decode('1E4'), + throwsFormatException, + ); // Scientific notation not allowed in decimal + }); + }); +} From a5936909ca91ca2310d24d57085b797ff75b9508 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 27 Nov 2025 07:15:28 +0000 Subject: [PATCH 2/3] Dart format --- lib/src/codecs/codecs.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/codecs/codecs.dart b/lib/src/codecs/codecs.dart index 7e65e13..e33555b 100644 --- a/lib/src/codecs/codecs.dart +++ b/lib/src/codecs/codecs.dart @@ -25,4 +25,4 @@ export 'g_month_day/gregorian_month_day_codec.dart'; export 'duration/xsd_duration_codec.dart'; export 'datetime/xsd_datetime_codec.dart'; export 'any_uri/any_uri_codec.dart'; -export 'decimal/decimal_codec.dart'; \ No newline at end of file +export 'decimal/decimal_codec.dart'; From 6008e584e21c1751e77c9b171c41e65c53014284 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 27 Nov 2025 07:19:57 +0000 Subject: [PATCH 3/3] Add additional test cases as per code review suggestion --- test/codecs/decimal_codec_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/codecs/decimal_codec_test.dart b/test/codecs/decimal_codec_test.dart index 32e1d1d..0b87550 100644 --- a/test/codecs/decimal_codec_test.dart +++ b/test/codecs/decimal_codec_test.dart @@ -16,6 +16,10 @@ void main() { expect(codec.decode('-123.45'), Decimal.parse('-123.45')); expect(codec.decode(' +123.45 '), Decimal.parse('123.45')); expect(codec.decode('.45'), Decimal.parse('0.45')); + expect(codec.decode('123'), Decimal.parse('123')); + expect(codec.decode('+123'), Decimal.parse('123')); + expect(codec.decode('-123'), Decimal.parse('-123')); + expect(codec.decode('123.'), Decimal.parse('123')); }); test('should throw on invalid decimal', () {