generic_reader 0.4.0 generic_reader: ^0.4.0 copied to clipboard
Extension providing methods for the systematic reading of enums, constant lists, maps, sets, and generic compile-time constant expressions.
Generic Reader - Example #
Retrieving Constants with Parameterized Type #
The file player_example.dart demonstrates how to use the library generic_reader
to read the value of a constant with parameterized type from a static representation of a
compile-time constant expression. The program also shows how to register Decoder
functions for the types Column
and SqliteType
.
The constant values that are going to be read are the fields of the const class Player
:
Click to show player.dart.
import 'package:test_types/test_types.dart';
/// Class modelling a player.
class Player {
const Player();
/// Column name
final columnName = 'Player';
/// Column storing player id.
final id = const Column<int>(defaultValue: 1, name: 'id');
/// Column storing first name of player.
final firstName = const Column<String>(
defaultValue: 'Thomas',
name: 'FirstName',
);
/// List of sponsors
final List<Sponsor> sponsors = const [
Sponsor('Johnson\'s'),
Sponsor('Smith Brothers'),
];
/// Test unregistered type.
final unregistered = const UnRegisteredTestType();
/// Test [Set<int>].
final Set<int> primeNumbers = const {1, 3, 5, 7, 11, 13};
/// Test enum
final Greek greek = Greek.alpha;
/// Test map
final map = const <String, dynamic>{'one': 1, 'two': 2.0};
/// Test map with enum entry
final mapWithEnumEntry = const <String, dynamic>{
'one': 1,
'two': 2.0,
'enum': Greek.alpha
};
/// Test list
final list = const <List<int>>[
[0, 1],
[10, 11]
];
}
In the simple example below, the function initializeLibraryReaderForDirectory
provided by source_gen_test
is used to load the source code and initialize objects of type LibraryReader
.
In a standard setting this task is delegated to a builder
that reads a builder configuration and loads the relevant assets.
Click to show player_example.dart.
import 'package:ansicolor/ansicolor.dart';
import 'package:exception_templates/exception_templates.dart';
import 'package:generic_reader/generic_reader.dart';
import 'package:source_gen/source_gen.dart' show ConstantReader;
import 'package:source_gen_test/source_gen_test.dart';
import 'package:test_types/test_types.dart';
/// To run this program navigate to the root folder
/// in your local copy the package `generic_reader` and
/// use the command:
///
/// # dart example/bin/player_example.dart
/// Demonstrates how to use [GenericReader] to read constants
/// with parameterized type from a static representation
/// of a compile-time constant expression
/// represented by a [ConstantReader].
Future<void> main() async {
/// Reading libraries.
print('Reading player.dart ...');
final playerLib = await initializeLibraryReaderForDirectory(
'example/src',
'player.dart',
);
print('Done reading player.dart');
// ConstantReader representing field 'columnName'.
final columnNameCR =
ConstantReader(playerLib.classes.first.fields[0].computeConstantValue());
final idCR =
ConstantReader(playerLib.classes.first.fields[1].computeConstantValue());
// ConstantReade representing field 'firstName'.
final firstNameCR =
ConstantReader(playerLib.classes.first.fields[2].computeConstantValue());
final sponsorsCR =
ConstantReader(playerLib.classes.first.fields[3].computeConstantValue());
final greekCR =
ConstantReader(playerLib.classes.first.fields[6].computeConstantValue());
final mapCR =
ConstantReader(playerLib.classes.first.fields[7].computeConstantValue());
final mapWithEnumEntryCR =
ConstantReader(playerLib.classes.first.fields[8].computeConstantValue());
final listCR =
ConstantReader(playerLib.classes.first.fields[9].computeConstantValue());
// Adding a decoder for constants of type [Column].
GenericReader.addDecoder<Column>((cr) {
final name = cr.read('name').get<String>();
if (cr.holdsA<Column<int>>()) {
final defaultValue = cr.read('defaultValue').get<int>();
return Column<int>(defaultValue: defaultValue, name: name);
}
if (cr.holdsA<Column<bool>>()) {
final defaultValue = cr.read('defaultValue').get<bool>();
return Column<bool>(defaultValue: defaultValue, name: name);
}
if (cr.holdsA<Column<String>>()) {
final defaultValue = cr.read('defaultValue').get<String>();
return Column<String>(defaultValue: defaultValue, name: name);
}
if (cr.holdsA<Column<double>>()) {
final defaultValue = cr.read('defaultValue').get<double>();
return Column<double>(defaultValue: defaultValue, name: name);
}
throw ErrorOf<Decoder<Column>>(
message: 'Error reading constant expression.',
expectedState: 'An instance of ConstantReader holding a '
'constant of type `Column`.');
});
final green = AnsiPen()..green(bold: true);
// Retrieve an instance of [String].
final columnName = columnNameCR.get<String>();
print(green('Retrieving a String:'));
print('columnName = \'$columnName\'');
print('');
// Prints:
// Retrieving a [String]
// columnName = 'Player'
// Retrieve an instance of [Column<Text>].
final columnFirstName = firstNameCR.get<Column>();
print(green('Retrieving a Column<Text>:'));
print(columnFirstName);
// Prints:
// Retrieving a [Column<Text>]:
// Column<Text>(
// defaultValue: Text('Thomas')
// )
// Adding a decoder function for type [Sponsor].
GenericReader.addDecoder<Sponsor>(
(cr) => Sponsor(cr.read('name').stringValue));
final sponsors = sponsorsCR.getList<Sponsor>();
print('');
print(green('Retrieving a List<Sponsor>:'));
print(sponsors);
// Prints:
// Retrieving a [List<Sponsor>]:
// [Sponsor: Johnson's, Sponsor: Smith Brothers]
final id = idCR.get<Column>();
print('');
print(green('Retrieving a Column<Integer>:'));
print(id);
// Prints:
// Retrieving a [Column<Integer>]:
// Column<Integer>(
// )
final greek = greekCR.get<Greek>();
print('');
print(green('Retrieving an instance of the '
'enumeration: Greek{alpha, beta}.'));
print(greek);
// Prints:
// 'Retrieving an instance of the enumeration: Greek{alpha, beta}.'
// Greek.alpha
final map = mapCR.getMap<String, dynamic>();
print('');
print(green('Retrieving a Map<String, dynamic>:'));
print(map);
// Prints:
// 'Retrieving a Map<String, dynamic>:'
// {one: 1, two: 2.0}
GenericReader.addDecoder<Greek>((cr) => cr.get<Greek>());
final mapWithEnumEntry = mapWithEnumEntryCR.getMap<String, dynamic>();
print('');
print(green('Retrieving a Map<String, dynamic>:'));
print(mapWithEnumEntry);
// Prints:
// 'Retrieving a Map<String, dynamic>:'
// {one: 1, two: 2.0, enum: Greek.alpha}
// Retrieving a nested list.
// Add a specific decoder for the inner type.
GenericReader.addDecoder<List<int>>((cr) => cr.getList<int>());
final list = listCR.getList<List<int>>();
print(green('\nRetrieving a List<List<int>>'));
print(list);
}
Retrieving Constants with Dynamic Type #
The example in the section above demonstrates how to retrieve constants with a known parameterized type.
The program presented below shows how to proceed if the constant has
a dynamic type parameter.
Note: The actual data-type must be either a bool
, double
, int
, num
, String
, Type
, Symbol
or a type with a registered decoder.
Consider the following generic class that wraps a value of type T
:
/// Wraps a variable of type [T].
class Wrapper<T> {
const Wrapper(this.value);
/// Value of type [T].
final T value;
@override
String toString() => 'Wrapper<$T>(value: $value)';
}
The type argument T
can assume any data-type and it is impractical
to handle all available types manually in the decoder function of Wrapper
.
Instead, one can use the method get<dynamic>()
.
This signals to the reader to match the static type
of the ConstantReader
instance to a registered data-type.
If a match is found get<dynamic>()
returns a constant
with the appropriate value, otherwise an error is thrown.
The program below retrieves the constant wrapper
defined in [wrapper_instance.dart].
Note the use of the method get<dynamic>()
when defining the Decoder function for
the data-type Wrapper
.
Click to show wrapper_example.dart.
import 'package:analyzer/dart/element/element.dart';
import 'package:ansicolor/ansicolor.dart';
import 'package:generic_reader/generic_reader.dart';
import 'package:source_gen/source_gen.dart'; // show ConstantReader;
import 'package:source_gen_test/src/init_library_reader.dart';
import 'package:test_types/test_types.dart';
/// To run this program navigate to the root folder
/// in your local copy the package `generic_reader` and
/// use the command:
///
/// # dart example/bin/wrapped_int_example.dart
/// Demonstrates how to use `GenericReader` to read constants
/// with parameterized type from a static representation
/// of a compile-time constant expression
/// represented by a `ConstantReader`.
Future<void> main() async {
/// Reading libraries.
final wrappedIntLib = await initializeLibraryReaderForDirectory(
'example/src',
'wrapper_instance.dart',
);
ConstantReader? wrapperCR;
for (var element in wrappedIntLib.allElements) {
if (element is TopLevelVariableElement) {
if (element.name == 'wrapper') {
wrapperCR = ConstantReader(element.computeConstantValue());
}
}
}
final green = AnsiPen()..green(bold: true);
// Adding a decoder function for type [Wrapper].
GenericReader.addDecoder<Wrapper>((ConstantReader cr) {
return Wrapper(cr.read('value').get<dynamic>());
});
print('');
print(green('Retrieving a Wrapper<dynamic>:'));
if (wrapperCR == null) {
print('Could not read constant of type Wrapper<dynamic>');
return;
}
final wrapper = wrapperCR.get<Wrapper>();
print(wrapper);
print(wrapper.value.runtimeType);
// Prints:
//
// Retrieving a [Wrapper<dynamic>]:
// Wrapper<dynamic>(value: 297)
// int
}
Features and bugs #
Please file feature requests and bugs at the issue tracker.