Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@
- Implement `xsd:decimal` codec
- Implement `xsd:double` codec
- Implement `xsd:float` codec
- Implement `xsd:unsignedInt` codec
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
18 changes: 18 additions & 0 deletions lib/src/codecs/unsigned_int/unsigned_int_codec.dart
Original file line number Diff line number Diff line change
@@ -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<int, String> {
const XsdUnsignedIntCodec();

@override
Converter<String, int> get decoder => const XsdUnsignedIntDecoder();

@override
Converter<int, String> get encoder => const XsdUnsignedIntEncoder();
}
35 changes: 35 additions & 0 deletions lib/src/codecs/unsigned_int/unsigned_int_decoder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'dart:convert';
import 'dart:typed_data';

import '../../helpers/whitespace.dart';

class XsdUnsignedIntDecoder extends Converter<String, int> {
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;
}
}
21 changes: 21 additions & 0 deletions lib/src/codecs/unsigned_int/unsigned_int_encoder.dart
Original file line number Diff line number Diff line change
@@ -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<int, String> {
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();
}
}
28 changes: 28 additions & 0 deletions test/codecs/unsigned_int_codec_test.dart
Original file line number Diff line number Diff line change
@@ -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);
});
}
Comment on lines +4 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The tests are comprehensive, which is great! To improve maintainability and reduce duplication, you can structure them using group and instantiate the codec and its decoder/encoder once outside the tests.

void main() {
  group('XSD Unsigned Int Codec', () {
    const codec = XsdUnsignedIntCodec();
    final decoder = codec.decoder;
    final encoder = codec.encoder;

    test('-> Decode', () {
      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('-> Encode', () {
      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);
    });
  });
}