Skip to content

Commit

Permalink
add SimpleMapper1Bounded and SimpleMapper2Bounded
Browse files Browse the repository at this point in the history
  • Loading branch information
Kilian Schulte committed Sep 25, 2024
1 parent 8ba8b51 commit 606ba7c
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 20 deletions.
34 changes: 31 additions & 3 deletions packages/dart_mappable/doc/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ class CustomBoxMapper extends SimpleMapper1<GenericBox> {
// use the type parameter [T] in the return type [GenericBox<T>]
GenericBox<T> decode<T>(dynamic value) {
// use the type parameter [T] in your decoding logic
T content = MapperContainer.globals.fromValue<T>(value);
return GenericBox<T>(content);
T content = container.fromValue<T>(value);
return GenericBox<T>(container.fromValue<T>(value));
}
@override
// use the type parameter [T] in the parameter type [GenericBox<T>]
dynamic encode<T>(GenericBox<T> self) {
// use the type parameter [T] in your encoding logic
return MapperContainer.globals.toValue<T>(self.content);
return container.toValue<T>(self.content);
}
// In case of generic types, we also must specify a type factory. This is a special type of
Expand All @@ -103,6 +103,34 @@ class CustomBoxMapper extends SimpleMapper1<GenericBox> {
}
```

If your generic class has one or two bounded type parameters (e.g. `class NumberBox<T extends num>`) use the `SimpleMapper1Bounded`
and `SimpleMapper2Bounded` variants:

```dart
class NumberBox<T extends num> {
NumberBox(this.content);
final T content;
}
class NumberBoxMapper extends SimpleMapper1Bounded<NumberBox, num> {
const NumberBoxMapper();
@override
NumberBox<A> decode<A extends num>(Object value) {
return NumberBox<A>(container.fromValue<A>(value));
}
@override
Object? encode<A extends num>(NumberBox<A> self) {
return container.toValue(self.content);
}
@override
Function get typeFactory => <T extends num>(f) => f<NumberBox<T>>();
}
```

## Custom Iterables and Maps

For special Iterable and Map types, you can of course specify custom mappers as described in the previous section.
Expand Down
74 changes: 57 additions & 17 deletions packages/dart_mappable/lib/src/mappers/simple_mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,92 @@ abstract class SimpleMapper<T extends Object> extends _SimpleMapperBase<T> {
}
}

/// An interface to define custom mappers for generic types with 1 argument.
/// An interface to define custom mappers for generic types with one bounded argument.
///
/// {@category Custom Mappers}
abstract class SimpleMapper1<T extends Object> extends _SimpleMapperBase<T> {
const SimpleMapper1();
abstract class SimpleMapper1Bounded<T extends Object, B1>
extends _SimpleMapperBase<T> {
const SimpleMapper1Bounded();

/// Override as `MyClass<A> decode<A>(Object value)`
T decode<A>(Object value);
/// Override as `MyClass<A> decode<A extends ...>(Object value)`
T decode<A extends B1>(Object value);

/// Override as `Object encode<A>(MyClass<A> self)`
Object? encode<A>(covariant T self);
/// Override as `Object? encode<A extends ...>(MyClass<A> self)`
Object? encode<A extends B1>(covariant T self);

@override
T _decode(Object value, DecodingContext context) {
return context.callWith1(decode, value);
assert(context.args.length == 1);
return context.callWith(decode, value);
}

@override
Object? _encode(T value, EncodingContext context) {
return context.callWith1(encode, value);
assert(context.args.length == 1);
return context.callWith(encode, value);
}
}

/// An interface to define custom mappers for generic types with 2 arguments.
/// An interface to define custom mappers for generic types with one argument.
///
/// For generic types with one bounded type argument extend the [SimpleMapper1Bounded]
/// interface instead.
///
/// {@category Custom Mappers}
abstract class SimpleMapper2<T extends Object> extends _SimpleMapperBase<T> {
const SimpleMapper2();
abstract class SimpleMapper1<T extends Object>
extends SimpleMapper1Bounded<T, dynamic> {
/// Override as `MyClass<A> decode<A>(Object value)`
@override
T decode<A>(Object value);

/// Override as `MyClass<A, B> decode<A, B>(Object value)`
T decode<A, B>(Object value);
/// Override as `Object? encode<A>(MyClass<A> self)`
@override
Object? encode<A>(covariant T self);
}

/// An interface to define custom mappers for generic types with two bounded arguments.
///
/// {@category Custom Mappers}
abstract class SimpleMapper2Bounded<T extends Object, B1, B2>
extends _SimpleMapperBase<T> {
const SimpleMapper2Bounded();

/// Override as `MyClass<A, B> decode<A extends ..., B extends ...>(Object value)`
T decode<A extends B1, B extends B2>(Object value);

/// Override as `Object? encode<A, B>(MyClass<A, B> self)`
Object? encode<A, B>(covariant T self);
Object? encode<A extends B1, B extends B2>(covariant T self);

@override
T _decode(Object value, DecodingContext context) {
return context.callWith2(decode, value);
assert(context.args.length == 2);
return context.callWith(decode, value);
}

@override
Object? _encode(T value, EncodingContext context) {
return context.callWith2(encode, value);
assert(context.args.length == 2);
return context.callWith(encode, value);
}
}

/// An interface to define custom mappers for generic types with two arguments.
///
/// For generic types with two bounded type arguments extend the [SimpleMapper2Bounded]
/// interface instead.
///
/// {@category Custom Mappers}
abstract class SimpleMapper2<T extends Object>
extends SimpleMapper2Bounded<T, dynamic, dynamic> {
/// Override as `MyClass<A, B> decode<A, B>(Object value)`
@override
T decode<A, B>(Object value);

/// Override as `Object? encode<A, B>(MyClass<A, B> self)`
@override
Object? encode<A, B>(covariant T self);
}

abstract class _SimpleMapperBase<T extends Object> extends MapperBase<T> {
const _SimpleMapperBase();

Expand Down
33 changes: 33 additions & 0 deletions packages/dart_mappable/test/custom_mapper/custom_mapper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,29 @@ class BigIntMapper extends PrimitiveMapper<BigInt> {

class EmptyMapper extends MapperBase<Uint8List> {}

class NumberBox<T extends num> {
NumberBox(this.content);

final T content;
}

class NumberBoxMapper extends SimpleMapper1Bounded<NumberBox, num> {
const NumberBoxMapper();

@override
Function get typeFactory => <T extends num>(f) => f<NumberBox<T>>();

@override
NumberBox<A> decode<A extends num>(Object value) {
return NumberBox<A>(container.fromValue<A>(value));
}

@override
Object? encode<A extends num>(NumberBox<A> self) {
return container.toValue(self.content);
}
}

void main() {
group('Custom Mappers', () {
test('Simple Custom Mapper', () {
Expand All @@ -115,6 +138,16 @@ void main() {
expect(json, equals('2'));
});

test('Bounded generic custom Mapper', () {
MapperContainer.globals.use(NumberBoxMapper());

NumberBox<double> box = MapperContainer.globals.fromValue('2.3');
expect(box.content, equals(2.3));

var json = MapperContainer.globals.toJson(box);
expect(json, equals('2.3'));
});

test('Custom Uri Mapper', () {
MapperContainer.globals.use(UriMapper());

Expand Down

0 comments on commit 606ba7c

Please sign in to comment.