diff --git a/CHANGELOG.md b/CHANGELOG.md index 172e8b4..4987633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,3 +31,4 @@ - Implement `xsd:decimal` codec - Implement `xsd:double` codec - Implement `xsd:float` codec +- Implement `xsd:unsignedInt` codec diff --git a/README.md b/README.md index 3880167..01c9f73 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ This library aims to support the following XSD 1.1 built-in datatypes that are c | `xsd:nonNegativeInteger` | ✅ | ✅ | `BigInt` | `dart:core` | | `xsd:positiveInteger` | ✅ | ✅ | `BigInt` | `dart:core` | | `xsd:unsignedLong` | ✅ | ✅ | `BigInt` | `dart:core` | -| `xsd:unsignedInt` | ✅ | ❌ | `int` | `dart:core` | +| `xsd:unsignedInt` | ✅ | ✅ | `int` | `dart:core` | | `xsd:unsignedShort` | ✅ | ✅ | `int` | `dart:core` | | `xsd:unsignedByte` | ✅ | ✅ | `int` | `dart:core` | | `xsd:anyURI` | ✅ | ✅ | `Uri` | `dart:core` | diff --git a/lib/src/codecs/unsigned_int/unsigned_int_codec.dart b/lib/src/codecs/unsigned_int/unsigned_int_codec.dart new file mode 100644 index 0000000..28e5b62 --- /dev/null +++ b/lib/src/codecs/unsigned_int/unsigned_int_codec.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +import 'package:xsd/src/codecs/unsigned_int/unsigned_int_decoder.dart'; +import 'package:xsd/src/codecs/unsigned_int/unsigned_int_encoder.dart'; + +/// A codec for xsd:unsignedInt. +/// +/// Converts between a string representation of an unsigned integer and an +/// [int]. +class XsdUnsignedIntCodec extends Codec { + const XsdUnsignedIntCodec(); + + @override + Converter get decoder => const XsdUnsignedIntDecoder(); + + @override + Converter get encoder => const XsdUnsignedIntEncoder(); +} diff --git a/lib/src/codecs/unsigned_int/unsigned_int_decoder.dart b/lib/src/codecs/unsigned_int/unsigned_int_decoder.dart new file mode 100644 index 0000000..f2e0a8e --- /dev/null +++ b/lib/src/codecs/unsigned_int/unsigned_int_decoder.dart @@ -0,0 +1,35 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../helpers/whitespace.dart'; + +class XsdUnsignedIntDecoder extends Converter { + const XsdUnsignedIntDecoder(); + + @override + int convert(String input) { + final str = processWhiteSpace(input, Whitespace.collapse); + + if (str.isEmpty) { + throw const FormatException('The input string cannot be empty.'); + } + + final value = int.tryParse(str); + + if (value == null) { + throw FormatException('The input "$str" is not a valid integer.'); + } + + // Use Uint32List to validate that the value fits within 32 bits (wraps on overflow). + final list = Uint32List(1); + list[0] = value; + + if (list[0] != value) { + throw FormatException( + 'The value "$value" is out of range for xsd:unsignedInt.', + ); + } + + return value; + } +} diff --git a/lib/src/codecs/unsigned_int/unsigned_int_encoder.dart b/lib/src/codecs/unsigned_int/unsigned_int_encoder.dart new file mode 100644 index 0000000..f5b3212 --- /dev/null +++ b/lib/src/codecs/unsigned_int/unsigned_int_encoder.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +/// A [Converter] that converts an [int] to an XSD `unsignedInt` string. +class XsdUnsignedIntEncoder extends Converter { + const XsdUnsignedIntEncoder(); + + @override + String convert(int input) { + // Use Uint32List to validate that the value fits within 32 bits (wraps on overflow). + final list = Uint32List(1); + list[0] = input; + if (list[0] != input) { + throw FormatException( + 'The value "$input" is out of range for xsd:unsignedInt.', + ); + } + + return input.toString(); + } +} diff --git a/test/codecs/unsigned_int_codec_test.dart b/test/codecs/unsigned_int_codec_test.dart new file mode 100644 index 0000000..5ce3af3 --- /dev/null +++ b/test/codecs/unsigned_int_codec_test.dart @@ -0,0 +1,28 @@ +import 'package:test/test.dart'; +import 'package:xsd/src/codecs/unsigned_int/unsigned_int_codec.dart'; + +void main() { + test('XSD Unsigned Int Codec -> Decode', () { + const codec = XsdUnsignedIntCodec(); + final decoder = codec.decoder; + + expect(decoder.convert('12345'), equals(12345)); + expect(decoder.convert('0'), equals(0)); + expect(decoder.convert('4294967295'), equals(4294967295)); + expect(() => decoder.convert('-1'), throwsFormatException); + expect(() => decoder.convert('4294967296'), throwsFormatException); + expect(() => decoder.convert('abc'), throwsFormatException); + expect(() => decoder.convert(''), throwsFormatException); + }); + + test('XSD Unsigned Int Codec -> Encode', () { + const codec = XsdUnsignedIntCodec(); + final encoder = codec.encoder; + + expect(encoder.convert(12345), equals('12345')); + expect(encoder.convert(0), equals('0')); + expect(encoder.convert(4294967295), equals('4294967295')); + expect(() => encoder.convert(-1), throwsFormatException); + expect(() => encoder.convert(4294967296), throwsFormatException); + }); +}