diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a19fd4..13bca20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,3 +21,4 @@ - Implement `xsd:gMonth` codec and data type - Implement `xsd:unsignedShort` data type codec - Implement `xsd:positiveInteger` data type codec +- Implement `xsd:nonPositiveInteger` data type codec diff --git a/README.md b/README.md index ef226d4..ebd2694 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ This library aims to support the following XSD 1.1 built-in datatypes that are c | `xsd:double` | ✅ | ❌ | `double` | `dart:core` | | `xsd:float` | ✅ | ❌ | `double` | `dart:core` | | `xsd:integer` | ✅ | ❌ | `BigInt` | `dart:core` | -| `xsd:nonPositiveInteger` | ✅ | ❌ | `BigInt` | `dart:core` | +| `xsd:nonPositiveInteger` | ✅ | ✅ | `BigInt` | `dart:core` | | `xsd:negativeInteger` | ✅ | ❌ | `BigInt` | `dart:core` | | `xsd:long` | ✅ | ✅ | `BigInt` | `dart:core` | | `xsd:int` | ✅ | ✅ | `int` | `dart:core` | diff --git a/lib/src/codecs/codecs.dart b/lib/src/codecs/codecs.dart index cabc6ea..08b7f95 100644 --- a/lib/src/codecs/codecs.dart +++ b/lib/src/codecs/codecs.dart @@ -18,3 +18,4 @@ export 'g_year_month/year_month_codec.dart'; export 'g_month/gregorian_month_codec.dart'; export 'unsigned_short/unsigned_short_codec.dart'; export 'positive_integer/positive_integer_codec.dart'; +export 'non_positive_integer/non_positive_integer_codec.dart'; diff --git a/lib/src/codecs/non_positive_integer/non_positive_integer_codec.dart b/lib/src/codecs/non_positive_integer/non_positive_integer_codec.dart new file mode 100644 index 0000000..6440623 --- /dev/null +++ b/lib/src/codecs/non_positive_integer/non_positive_integer_codec.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +import 'package:xsd/src/codecs/non_positive_integer/non_positive_integer_decoder.dart'; +import 'package:xsd/src/codecs/non_positive_integer/non_positive_integer_encoder.dart'; + +/// A codec for xsd:nonPositiveInteger. +/// +/// Converts between a string representation of a non-positive integer and a +/// [BigInt]. +class XsdNonPositiveIntegerCodec extends Codec { + const XsdNonPositiveIntegerCodec(); + + @override + Converter get decoder => const XsdNonPositiveIntegerDecoder(); + + @override + Converter get encoder => const XsdNonPositiveIntegerEncoder(); +} diff --git a/lib/src/codecs/non_positive_integer/non_positive_integer_decoder.dart b/lib/src/codecs/non_positive_integer/non_positive_integer_decoder.dart new file mode 100644 index 0000000..f028a8f --- /dev/null +++ b/lib/src/codecs/non_positive_integer/non_positive_integer_decoder.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; + +import '../../helpers/whitespace.dart'; + +class XsdNonPositiveIntegerDecoder extends Converter { + const XsdNonPositiveIntegerDecoder(); + + static final BigInt _maxValue = BigInt.zero; + + @override + BigInt convert(String input) { + final str = processWhiteSpace(input, Whitespace.collapse); + + if (str.isEmpty) { + throw const FormatException('The input string cannot be empty.'); + } + + final value = BigInt.tryParse(str); + + if (value == null) { + throw FormatException('The input "$str" is not a valid integer.'); + } + + if (value > _maxValue) { + throw FormatException( + 'The value "$value" must be less than or equal to $_maxValue.', + ); + } + + return value; + } +} diff --git a/lib/src/codecs/non_positive_integer/non_positive_integer_encoder.dart b/lib/src/codecs/non_positive_integer/non_positive_integer_encoder.dart new file mode 100644 index 0000000..47bb3fb --- /dev/null +++ b/lib/src/codecs/non_positive_integer/non_positive_integer_encoder.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +class XsdNonPositiveIntegerEncoder extends Converter { + const XsdNonPositiveIntegerEncoder(); + + static final BigInt _maxValue = BigInt.zero; + + @override + String convert(BigInt input) { + if (input > _maxValue) { + throw FormatException( + 'The value "$input" must be less than or equal to $_maxValue.', + ); + } + + return input.toString(); + } +} diff --git a/test/codecs/non_positive_integer_codec_test.dart b/test/codecs/non_positive_integer_codec_test.dart new file mode 100644 index 0000000..004096c --- /dev/null +++ b/test/codecs/non_positive_integer_codec_test.dart @@ -0,0 +1,56 @@ +import 'package:test/test.dart'; +import 'package:xsd/xsd.dart'; + +void main() { + group('XsdNonPositiveIntegerCodec', () { + const codec = XsdNonPositiveIntegerCodec(); + + group('decoder', () { + test('should decode a valid non-positive integer string', () { + expect(codec.decode('0'), BigInt.zero); + expect(codec.decode('-1'), BigInt.from(-1)); + expect(codec.decode('-123'), BigInt.from(-123)); + expect( + codec.decode( + '-9999999999999999999999999999999999999999999999999999999999', + ), + BigInt.parse( + '-9999999999999999999999999999999999999999999999999999999999', + ), + ); + }); + + test('should throw a FormatException for a positive integer', () { + expect(() => codec.decode('1'), throwsFormatException); + }); + + test('should throw a FormatException for an empty string', () { + expect(() => codec.decode(''), throwsFormatException); + }); + + test('should throw a FormatException for a non-integer string', () { + expect(() => codec.decode('abc'), throwsFormatException); + }); + }); + + group('encoder', () { + test('should encode a valid non-positive integer', () { + expect(codec.encode(BigInt.zero), '0'); + expect(codec.encode(BigInt.from(-1)), '-1'); + expect(codec.encode(BigInt.from(-123)), '-123'); + expect( + codec.encode( + BigInt.parse( + '-9999999999999999999999999999999999999999999999999999999999', + ), + ), + '-9999999999999999999999999999999999999999999999999999999999', + ); + }); + + test('should throw a FormatException for a positive integer', () { + expect(() => codec.encode(BigInt.one), throwsFormatException); + }); + }); + }); +}