Skip to content

Commit 1cd9bc1

Browse files
committed
Extension GenericReader was renamed Reader.
1 parent 0f48988 commit 1cd9bc1

File tree

3 files changed

+471
-11
lines changed

3 files changed

+471
-11
lines changed

lib/generic_reader.dart

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
/// Library providing a customizable generic reader aimed at creating
2-
/// runtime constant objects from a static representation
3-
/// of a compile-time constant expression such as [ConstantReader]
4-
/// or [DartObject].
5-
library;
6-
7-
export 'src/extensions/generic_reader.dart';
8-
export 'src/extensions/type_methods.dart';
9-
export 'src/error_types/invalid_type_argument.dart';
10-
export 'src/types/decoder.dart';
11-
export 'src/types/unknown_type.dart';
1+
export 'src/decoder/core_decoder.dart';
2+
export 'src/decoder/decoder.dart';
3+
export 'src/reader.dart';
4+
export 'src/extension/type_methods.dart';
5+
export 'src/type/decoder_not_found.dart';
6+
export 'src/type/invalid_type_argument.dart';

lib/src/reader.dart

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
import 'dart:collection';
2+
3+
import 'package:analyzer/dart/constant/value.dart' show DartObject;
4+
import 'package:analyzer/dart/element/type.dart' show DartType;
5+
import 'package:exception_templates/exception_templates.dart';
6+
7+
import 'decoder/core_decoder.dart';
8+
import 'decoder/decoder.dart';
9+
import 'type/decoder_not_found.dart';
10+
import 'type/invalid_field_name.dart';
11+
import 'type/invalid_type_argument.dart' show InvalidTypeArgument;
12+
import 'extension/type_methods.dart';
13+
14+
part 'decoder/collection_decoder.dart';
15+
16+
extension Reader on DartObject {
17+
static final Map<Type, Decoder> _decoders = <Type, Decoder>{
18+
bool: boolDecoder,
19+
double: doubleDecoder,
20+
int: intDecoder,
21+
num: numDecoder,
22+
String: stringDecoder,
23+
Symbol: symbolDecoder,
24+
Type: typeDecoder,
25+
};
26+
static final Map<DartType, Type> _resolvedTypes = {};
27+
28+
/// Returns an [UnmodifiableMapView] of the currently registered decoders.
29+
static Map<Type, Decoder> get decoders => UnmodifiableMapView(_decoders);
30+
31+
/// Returns an [UnmodifiableMapView] of the currently resolved types.
32+
static Map<DartType, Type> get resolvedTypes =>
33+
UnmodifiableMapView(_resolvedTypes);
34+
35+
/// Returns `true` if there is a decoder for type [T].
36+
static bool hasDecoder<T>() => _decoders.containsKey(T);
37+
38+
/// Returns a set including the core types:
39+
/// bool, double, dynamic, int, num, String, Symbol, Type,
40+
/// Decoders for (exactly) these types cannot be registered or cleared.
41+
static Set<Type> coreTypes = Set.unmodifiable({
42+
bool,
43+
double,
44+
dynamic,
45+
int,
46+
num,
47+
String,
48+
Symbol,
49+
Type,
50+
});
51+
52+
/// Returns `true` if [T] belongs to bool,
53+
/// double, dynamic, int, num, String, Symbol, Type,
54+
/// and `false` otherwise.
55+
static bool isCoreType<T>() => coreTypes.contains(T);
56+
57+
/// Attempts to remove [decoder] from the set of registered decoders and
58+
/// returns `true` on success, and `false` otherwise.
59+
/// Note: Decoders that cannot be cleared handle the following types:
60+
/// - `bool`, `double`, `int`, `Null`,`num`,`Symbol`,`Type`,`dynamic`.
61+
static Decoder<T>? removeDecoderFor<T>() {
62+
if (isCoreType<T>()) {
63+
return null;
64+
} else {
65+
return _decoders.remove(T) as Decoder<T>;
66+
}
67+
}
68+
69+
/// Adds or updates a decoder function for type [T].
70+
/// Returns `true` if the decoder was added.
71+
/// Note: Decoders for the following types can not be added
72+
/// or updated:
73+
/// - `bool`, `double`, `int`, `Null`,`num`,`Symbol`,`Type`,
74+
/// - `dynamic`.
75+
static bool addDecoder<T>(Decoder<T> decoder) {
76+
if (isCoreType<T>()) return false;
77+
if (hasDecoder<T>()) return false;
78+
// Adding Decoder function.
79+
_decoders[T] = decoder;
80+
return true;
81+
}
82+
83+
/// Attemps to find a decoder that can decode type [T] and returns it.
84+
/// Return `null` if no suitable decoder was found.
85+
static Decoder<T>? findDecoder<T>() {
86+
if (_decoders.containsKey(T)) {
87+
return _decoders[T]! as Decoder<T>;
88+
}
89+
for (final decoder in _decoders.values) {
90+
if (decoder.canDecode<T>()) {
91+
// Just add decoders. Don't overwrite existing ones.
92+
_decoders[T] ??= decoder;
93+
return _decoders[T]! as Decoder<T>;
94+
}
95+
}
96+
return null;
97+
}
98+
99+
/// Reads the [DartObject] and returns an instance of [T].
100+
/// * If the argument [fieldName] is not empty,
101+
/// it reads the field of the object instead and throws
102+
/// [ErrorOfType] with type argument [InvalidFieldName] if the field does
103+
/// not exist.
104+
/// * Throws [ErrorOfType] with type argument [DecoderNotFound] on failure.
105+
T read<T>({String fieldName = ''}) {
106+
if (fieldName.isNotEmpty) {
107+
final fieldObj = getField(fieldName);
108+
if (fieldObj != null) {
109+
return fieldObj.read<T>();
110+
} else {
111+
throw invalidFieldNameError<T>(fieldName: fieldName);
112+
}
113+
}
114+
if (T == dynamic) return _readDynamic();
115+
final decoder = findDecoder<T>();
116+
if (decoder != null) {
117+
return decoder.read(this);
118+
} else {
119+
throw decoderNotFoundError<T>();
120+
}
121+
}
122+
123+
/// Reads a constant with type `dynamic`.
124+
/// Throws [ErrorOfType] with type argument [DecoderNotFound] on failure.
125+
dynamic _readDynamic() {
126+
if (type == null) {
127+
return null;
128+
} else {
129+
if (_resolvedTypes[type] != null) {
130+
final rType = _resolvedTypes[type]!;
131+
return _decoders[rType]?.read(this);
132+
}
133+
134+
// Try your best
135+
for (final rType in _decoders.keys) {
136+
try {
137+
final result = _decoders[rType]?.read(this);
138+
// Store resolved type:
139+
_resolvedTypes[type!] = rType;
140+
return result;
141+
} on ErrorOf<Decoder> {
142+
//print('Read a constant <$type> using Decoder<$rType> failed.');
143+
}
144+
}
145+
throw decoderNotFoundError();
146+
}
147+
}
148+
149+
/// Reads the `dartObject` instance and returns an instance of `List<T>`.
150+
/// * If the argument [fieldName] is not empty,
151+
/// it reads the field of the object instead and throws
152+
/// [ErrorOfType] with type argument [InvalidFieldName] if the field does
153+
/// not exist.
154+
/// * Throws [ErrorOf] with type argument [Decoder] with type argument
155+
/// [List] with type argument [T] if a list can not be constructed.
156+
List<T> readList<T>({String fieldName = ''}) {
157+
if (fieldName.isNotEmpty) {
158+
final fieldObj = getField(fieldName);
159+
if (fieldObj == null) {
160+
throw invalidFieldNameError<T>(fieldName: fieldName);
161+
} else {
162+
return fieldObj.readList<T>();
163+
}
164+
}
165+
if (isNotList) {
166+
throw invalidArgumentTypeError<List<T>>();
167+
}
168+
169+
final result = toListValue()?.map((item) => item.read<T>()).toList();
170+
if (result == null) {
171+
throw isNullError<List<T>>();
172+
} else {
173+
return result;
174+
}
175+
}
176+
177+
/// Reads the `dartObject` instance and returns an instance of `List<T>`.
178+
/// * If the argument [fieldName] is not empty,
179+
/// it reads the field of the object instead and throws
180+
/// [ErrorOfType] with type argument [InvalidFieldName] if the field does
181+
/// not exist.
182+
/// * Throws [ErrorOf] with type argument [Decoder] with type argument
183+
/// [Iterable] with type argument [T] if an iterable can not be constructed.
184+
Iterable<T> readIterable<T>({String fieldName = ''}) {
185+
if (fieldName.isNotEmpty) {
186+
final fieldObj = getField(fieldName);
187+
if (fieldObj == null) {
188+
throw invalidFieldNameError<T>(fieldName: fieldName);
189+
} else {
190+
return fieldObj.readIterable<T>();
191+
}
192+
}
193+
if (isNotList) {
194+
throw invalidArgumentTypeError<Iterable<T>>();
195+
}
196+
197+
final result = toListValue()?.map((item) => item.read<T>());
198+
if (result == null) {
199+
throw isNullError<List<T>>();
200+
} else {
201+
return result;
202+
}
203+
}
204+
205+
/// Reads the `dartObject` instance and returns an object of type `Set<T>`.
206+
/// * If the argument [fieldName] is not empty,
207+
/// it reads the field of the object instead and throws
208+
/// [ErrorOfType] with type argument [InvalidFieldName] if the field does
209+
/// not exist.
210+
/// * Throws [ErrorOf] with type argument [Decoder] with type argument
211+
/// [Iterable] with type argument [T] if an iterable can not be constructed.
212+
Set<T> readSet<T>({String fieldName = ''}) {
213+
if (fieldName.isNotEmpty) {
214+
final fieldObj = getField(fieldName);
215+
if (fieldObj == null) {
216+
throw invalidFieldNameError<Set<T>>(fieldName: fieldName);
217+
} else {
218+
return fieldObj.readSet<T>();
219+
}
220+
}
221+
if (isNotSet) {
222+
throw invalidArgumentTypeError<Set<T>>();
223+
}
224+
final result = toSetValue()?.map((item) => item.read<T>()).toSet();
225+
if (result == null) {
226+
throw isNullError<Set<T>>();
227+
} else {
228+
return result;
229+
}
230+
}
231+
232+
/// Reads the [DartObject] and returns an object of type `Map<K, V>`.
233+
/// * If the argument [fieldName] is not empty,
234+
/// it reads the field of the object instead and throws
235+
/// [ErrorOfType] with type argument [InvalidFieldName] if the field does
236+
/// not exist.
237+
/// * Throws an instance of [ErrorOf] with type argument `Decoder<Map<K, V>>`
238+
/// if the map cannot be constructed.
239+
Map<K, V> readMap<K, V>({String fieldName = ''}) {
240+
if (fieldName.isNotEmpty) {
241+
final fieldObj = getField(fieldName);
242+
if (fieldObj == null) {
243+
throw invalidFieldNameError<Map<K, V>>(fieldName: fieldName);
244+
} else {
245+
return fieldObj.readMap<K, V>();
246+
}
247+
}
248+
if (isNotMap) {
249+
throw invalidArgumentTypeError<Map<K, V>>();
250+
}
251+
252+
final result = <K, V>{};
253+
254+
final mapObj = toMapValue();
255+
if (mapObj == null) {
256+
throw isNullError<Map<K, V>>();
257+
} else {
258+
mapObj.forEach((keyObj, valueObj) {
259+
final key = keyObj?.read<K>();
260+
final value = valueObj?.read<V>();
261+
262+
if (key is K && value is V) {
263+
result[key] = value;
264+
}
265+
});
266+
return result;
267+
}
268+
}
269+
270+
static String get info {
271+
return 'Reader:\n'
272+
' Registered types: ${_decoders.keys}\n'
273+
' Mapped types : $_resolvedTypes';
274+
}
275+
276+
ErrorOfType<InvalidTypeArgument> invalidArgumentTypeError<T>() =>
277+
throw ErrorOfType<InvalidTypeArgument>(
278+
message: 'Could not read constant $T.',
279+
invalidState: 'DartObject $this does not represent a $T.',
280+
expectedState: 'A DartObject that represents a $T.',
281+
);
282+
283+
ErrorOfType<InvalidTypeArgument> isNullError<T>() =>
284+
throw ErrorOfType<InvalidTypeArgument>(
285+
message: 'Could not read constant $T.',
286+
invalidState: 'DartObject $this represents null.',
287+
expectedState: 'A DartObject that represents a $T.',
288+
);
289+
290+
ErrorOfType<InvalidFieldName> invalidFieldNameError<T>({
291+
required String fieldName,
292+
}) => throw ErrorOfType<InvalidFieldName>(
293+
message: 'Could not read a field with name: $fieldName.',
294+
invalidState: 'DartObject $this does not seem to have a field $fieldName.',
295+
expectedState: 'Class $T should have a variable with name: $fieldName .',
296+
);
297+
298+
ErrorOfType<DecoderNotFound> decoderNotFoundError<T>() {
299+
final rType = (T == dynamic) ? type : T;
300+
301+
return ErrorOfType<DecoderNotFound>(
302+
message: 'Decoder not found.',
303+
invalidState: 'A decoder for type $rType is missing.',
304+
expectedState:
305+
'Use addDecoder<A>(decoder) to register '
306+
'a decoder for type $rType.',
307+
);
308+
}
309+
}

0 commit comments

Comments
 (0)