Skip to content

Commit 0f48988

Browse files
committed
Decoder is now a class.
1 parent 2823fa3 commit 0f48988

File tree

5 files changed

+279
-0
lines changed

5 files changed

+279
-0
lines changed

example/bin/decoder_example.dart

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import 'package:analyzer/dart/constant/value.dart';
2+
import 'package:ansi_modifier/ansi_modifier.dart';
3+
import 'package:build_test/build_test.dart' show resolveSource;
4+
import 'package:generic_reader/generic_reader.dart';
5+
6+
/// To run this program navigate to the root folder
7+
/// in your local copy the package `generic_reader` and
8+
/// use the command:
9+
///
10+
/// # dart example/bin/decoder_example.dart
11+
12+
/// Demonstrates how to use [Reader] to read constants.
13+
class A {
14+
const A({required this.id, required this.names, required this.numbers});
15+
final int id;
16+
final Set<String> names;
17+
final List<num> numbers;
18+
19+
@override
20+
String toString() =>
21+
'A(id: $id, names: $names, '
22+
'numbers: $numbers )';
23+
}
24+
25+
final a = const A(id: 42, names: {'Andy', 'Eva'}, numbers: [42, 3.14]);
26+
27+
class DecoderForClassA extends Decoder<A> {
28+
const DecoderForClassA();
29+
@override
30+
A read(DartObject obj) {
31+
final id = obj.read<int>(fieldName: 'id');
32+
final names = obj.readSet<String>(fieldName: 'names');
33+
final numbers = obj.readList<num>(fieldName: 'numbers');
34+
return A(id: id, names: names, numbers: numbers);
35+
}
36+
}
37+
38+
const decoderForA = DecoderForClassA();
39+
40+
Future<void> main() async {
41+
final library = await resolveSource(
42+
r'''
43+
library example;
44+
45+
class A {
46+
const A({required this.id, required this.names, required this.numbers});
47+
final int id;
48+
final Set<String> names;
49+
final List<num> numbers;
50+
}
51+
52+
class B{
53+
const B();
54+
final int id = 124;
55+
final Map<String, int> score = {'Adam': 4, 'Moira': 7};
56+
final isValid = true;
57+
58+
final a = const A(id: 42, names: {'Andy', 'Eva'}, numbers: [42, 3.14]);
59+
}
60+
61+
''',
62+
(resolver) => resolver.findLibraryByName('example'),
63+
//readAllSourcesFromFilesystem: false,
64+
);
65+
66+
/// Reading libraries.
67+
print('\nReading library <example>\n');
68+
69+
final lib = library!;
70+
71+
final idObj = lib.classes[1].fields[0].computeConstantValue();
72+
final id = idObj?.read<int>();
73+
print('Reading an ${'int:'.style(Ansi.green)} $id\n');
74+
75+
final scoreObj = lib.classes[1].fields[1].computeConstantValue();
76+
final score = scoreObj?.readMap<String, int>();
77+
print(
78+
'Reading a ${'Map<String, int>'.style(Ansi.green)}: '
79+
'$score ${score.runtimeType}\n',
80+
);
81+
82+
final isValidObj = lib.classes[1].fields[2].computeConstantValue();
83+
final isValid = isValidObj?.read<bool>();
84+
print(
85+
'Reading a ${'bool'.style(Ansi.green)}: $isValid ${isValid.runtimeType}\n',
86+
);
87+
88+
/// Adding a decoder:
89+
Reader.addDecoder(decoderForA);
90+
91+
final aObj = lib.classes[1].fields[3].computeConstantValue();
92+
final a = aObj?.read<A>();
93+
94+
print('Reading a constant with type ${'A'.style(Ansi.green)}: $a');
95+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
part of '../reader.dart';
2+
3+
class ListDecoder<T> extends Decoder<List<T>> {
4+
const ListDecoder();
5+
6+
@override
7+
List<T> read(DartObject obj) =>
8+
switch (obj.toListValue()?.map((item) => item.read<T>())) {
9+
Iterable<T> iterable => iterable.toList(),
10+
_ => throw readError(obj),
11+
};
12+
}
13+
14+
class SetDecoder<T> extends Decoder<Set<T>> {
15+
const SetDecoder({this.prototype = const {}});
16+
final Set<T> prototype;
17+
18+
@override
19+
Set<T> read(DartObject obj) =>
20+
switch (obj.toSetValue()?.map((item) => item.read<T>())) {
21+
Iterable<T> iterable => iterable.toSet(),
22+
_ => throw readError(obj),
23+
};
24+
}
25+
26+
class IterableDecoder<T> extends Decoder<Iterable<T>> {
27+
const IterableDecoder({this.prototype = const {}});
28+
final Set<T> prototype;
29+
30+
@override
31+
Set<T> read(DartObject obj) =>
32+
switch (obj.toSetValue()?.map((item) => item.read<T>())) {
33+
Iterable<T> iterable => iterable.toSet(),
34+
_ => throw readError(obj),
35+
};
36+
}

lib/src/decoder/core_decoder.dart

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import 'package:analyzer/dart/constant/value.dart' show DartObject;
2+
3+
import '../extension/type_methods.dart';
4+
import 'decoder.dart';
5+
6+
class BoolDecoder extends Decoder<bool> {
7+
const BoolDecoder();
8+
@override
9+
bool read(DartObject obj) => switch (obj.toBoolValue()) {
10+
bool value => value,
11+
_ => throw readError(obj),
12+
};
13+
}
14+
15+
const boolDecoder = BoolDecoder();
16+
17+
class IntDecoder extends Decoder<int> {
18+
const IntDecoder();
19+
@override
20+
int read(DartObject obj) => switch (obj.toIntValue()) {
21+
int value => value,
22+
_ => throw readError(obj),
23+
};
24+
}
25+
26+
const intDecoder = IntDecoder();
27+
28+
class DoubleDecoder extends Decoder<double> {
29+
const DoubleDecoder();
30+
@override
31+
double read(DartObject obj) => switch (obj.toDoubleValue()) {
32+
double value => value,
33+
_ => throw readError(obj),
34+
};
35+
}
36+
37+
const doubleDecoder = DoubleDecoder();
38+
39+
class NumDecoder extends Decoder<num> {
40+
const NumDecoder();
41+
@override
42+
num read(DartObject obj) {
43+
if (obj.isInt) {
44+
return intDecoder.read(obj);
45+
} else if (obj.isDouble) {
46+
return doubleDecoder.read(obj);
47+
} else {
48+
throw readError(obj);
49+
}
50+
}
51+
}
52+
53+
const numDecoder = NumDecoder();
54+
55+
class StringDecoder extends Decoder<String> {
56+
const StringDecoder();
57+
@override
58+
String read(DartObject obj) => switch (obj.toStringValue()) {
59+
String value => value,
60+
_ => throw readError(obj),
61+
};
62+
}
63+
64+
const stringDecoder = StringDecoder();
65+
66+
class SymbolDecoder extends Decoder<Symbol> {
67+
const SymbolDecoder();
68+
@override
69+
Symbol read(DartObject obj) => switch (obj.toSymbolValue()) {
70+
String value => Symbol(value),
71+
_ => throw readError(obj),
72+
};
73+
}
74+
75+
const symbolDecoder = SymbolDecoder();
76+
77+
class TypeDecoder extends Decoder<Type> {
78+
const TypeDecoder();
79+
@override
80+
Type read(DartObject obj) => switch (obj.toTypeValue()) {
81+
Type type => type,
82+
_ => throw readError(obj),
83+
};
84+
}
85+
86+
const typeDecoder = TypeDecoder();
87+
88+
/// A generic enum decoder
89+
class EnumDecoder<E extends Enum> extends Decoder<E> {
90+
const EnumDecoder(this.values);
91+
92+
/// Use the static enum getter to obtain the values.
93+
final List<E> values;
94+
95+
@override
96+
E read(DartObject obj) => switch (obj.getField('index')?.toIntValue()) {
97+
int index when index < values.length => values[index],
98+
_ => throw readError(obj),
99+
};
100+
}

lib/src/decoder/decoder.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'package:analyzer/dart/constant/value.dart' show DartObject;
2+
import 'package:exception_templates/exception_templates.dart' show ErrorOf;
3+
4+
import '../type/type_utils.dart';
5+
6+
abstract class Decoder<T> {
7+
const Decoder();
8+
9+
/// Returns `true` if this decoder can read instances of
10+
/// [DartObject] representing type [S], and `false` otherwise.
11+
bool canDecode<S>() => isSubType<S, T>();
12+
13+
/// Attempts to create an instance of [T] with information read from
14+
/// [obj].
15+
///
16+
/// Should throw [ErrorOf] with type argument [Decoder] with type argument
17+
/// [T] if an instance of [T] cannot be read from [obj].
18+
T read(DartObject obj);
19+
20+
/// Returns the generic type of the decoder.
21+
Type get type => T;
22+
23+
/// Error thrown if [obj] does not hold a variable of type [T].
24+
ErrorOf<Decoder<T>> readError(DartObject obj) {
25+
return ErrorOf<Decoder<T>>(
26+
message: 'Error reading const <$T> value.',
27+
invalidState:
28+
'obj holds a variable with static type: '
29+
'${obj.type}',
30+
expectedState:
31+
'The parameter \'obj\' must hold a '
32+
'constant with type <$T>.',
33+
);
34+
}
35+
}

test/decoder_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'package:generic_reader/generic_reader.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() async {
5+
group('numDecoder:', () {
6+
test('canDecode<int>()', () {
7+
expect(numDecoder.canDecode<int>(), true);
8+
});
9+
test('canDecode<double>()', () {
10+
expect(numDecoder.canDecode<double>(), true);
11+
});
12+
});
13+
}

0 commit comments

Comments
 (0)