Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullsafety #38

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: dart
dart:
- stable
- beta

sudo: required
env:
Expand Down
2 changes: 1 addition & 1 deletion lib/safe_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ library safe_config;

export 'src/compiler.dart';
export 'src/configuration.dart';
export 'src/default_configurations.dart';
export 'src/default_configurations.dart';
4 changes: 2 additions & 2 deletions lib/src/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'dart:mirrors';

import 'package:runtime/runtime.dart';

import 'configuration.dart';
import 'runtime.dart';
import 'package:safe_config/src/configuration.dart';
import 'package:safe_config/src/runtime.dart';

class ConfigurationCompiler extends Compiler {
@override
Expand Down
26 changes: 15 additions & 11 deletions lib/src/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ abstract class Configuration {

/// [contents] must be YAML.
Configuration.fromString(String contents) {
final yamlMap = loadYaml(contents) as Map<dynamic, dynamic>;
final yamlMap = loadYaml(contents) as Map<dynamic, dynamic>?;
final map =
yamlMap.map<String, dynamic>((k, v) => MapEntry(k.toString(), v));
yamlMap?.map<String, dynamic>((k, v) => MapEntry(k.toString(), v));
decode(map);
}

Expand All @@ -41,7 +41,7 @@ abstract class Configuration {
this, "input is not an object (is a '${value.runtimeType}')");
}

_runtime.decode(this, value as Map);
_runtime.decode(this, value);

validate();
}
Expand Down Expand Up @@ -74,12 +74,16 @@ abstract class ConfigurationRuntime {
void decode(Configuration configuration, Map input);
void validate(Configuration configuration);

dynamic tryDecode(Configuration configuration, String name, void decode()) {
dynamic tryDecode(
Configuration configuration,
String name,
dynamic decode(),
) {
try {
return decode();
} on ConfigurationException catch (e) {
throw ConfigurationException(configuration, e.message,
keyPath: [name]..addAll(e.keyPath));
keyPath: [name]..addAll(e.keyPath));
} on IntermediateException catch (e) {
final underlying = e.underlying;
if (underlying is ConfigurationException) {
Expand All @@ -89,17 +93,17 @@ abstract class ConfigurationRuntime {
underlying.keyPath
].expand((i) => i).toList();
throw ConfigurationException(configuration, underlying.message,
keyPath: keyPaths);
} else if (underlying is CastError) {
keyPath: keyPaths);
} else if (underlying is TypeError) {
throw ConfigurationException(configuration, "input is wrong type",
keyPath: [name]..addAll(e.keyPath));
keyPath: [name]..addAll(e.keyPath));
}

throw ConfigurationException(configuration, underlying.toString(),
keyPath: [name]..addAll(e.keyPath));
keyPath: [name]..addAll(e.keyPath));
} catch (e) {
throw ConfigurationException(configuration, e.toString(),
keyPath: [name]);
keyPath: [name]);
}
}
}
Expand Down Expand Up @@ -153,7 +157,7 @@ class ConfigurationException {

@override
String toString() {
if (keyPath?.isEmpty ?? true) {
if (keyPath.isEmpty) {
return "Failed to read '${configuration.runtimeType}'\n\t-> $message";
}

Expand Down
34 changes: 16 additions & 18 deletions lib/src/default_configurations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,37 @@ class DatabaseConfiguration extends Configuration {
/// The host of the database to connect to.
///
/// This property is required.
String host;
String? host;

/// The port of the database to connect to.
///
/// This property is required.
int port;
int? port;

/// The name of the database to connect to.
///
/// This property is required.
String databaseName;
String? databaseName;

/// A username for authenticating to the database.
///
/// This property is optional.
@optionalConfiguration
String username;
String? username;

/// A password for authenticating to the database.
///
/// This property is optional.
@optionalConfiguration
String password;
String? password;

/// A flag to represent permanence.
///
/// This flag is used for test suites that use a temporary database to run tests against,
/// dropping it after the tests are complete.
/// This property is optional.
@optionalConfiguration
bool isTemporary;
bool? isTemporary;

@override
void decode(dynamic value) {
Expand All @@ -68,26 +68,24 @@ class DatabaseConfiguration extends Configuration {
"'${value.runtimeType}' is not assignable; must be a object or string");
}

var uri = Uri.parse(value as String);
var uri = Uri.parse(value);
host = uri.host;
port = uri.port;
if (uri.pathSegments.length == 1) {
databaseName = uri.pathSegments.first;
}

if (uri.userInfo == null || uri.userInfo == '') {
if (uri.userInfo == '') {
validate();
return;
}

var authority = uri.userInfo.split(":");
if (authority != null) {
if (authority.isNotEmpty) {
username = Uri.decodeComponent(authority.first);
}
if (authority.length > 1) {
password = Uri.decodeComponent(authority.last);
}
if (authority.isNotEmpty) {
username = Uri.decodeComponent(authority.first);
}
if (authority.length > 1) {
password = Uri.decodeComponent(authority.last);
}

validate();
Expand All @@ -108,17 +106,17 @@ class APIConfiguration extends Configuration {
///
/// This property is required.
/// Example: https://external.api.com:80/resources
String baseURL;
String? baseURL;

/// The client ID.
///
/// This property is optional.
@optionalConfiguration
String clientID;
String? clientID;

/// The client secret.
///
/// This property is optional.
@optionalConfiguration
String clientSecret;
String? clientSecret;
}
39 changes: 20 additions & 19 deletions lib/src/mirror_property.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ class MirrorTypeCodec {
final klass = type as ClassMirror;
final classHasDefaultConstructor = klass.declarations.values.any((dm) {
return dm is MethodMirror &&
dm.isConstructor &&
dm.constructorName == const Symbol('') &&
dm.parameters.every((p) => p.isOptional == true);
dm.isConstructor &&
dm.constructorName == const Symbol('') &&
dm.parameters.every((p) => p.isOptional == true);
});

if (!classHasDefaultConstructor) {
throw StateError(
"Failed to compile '${type.reflectedType}'\n\t-> 'Configuration' subclasses MUST declare an unnammed constructor (i.e. '${type.reflectedType}();') if they are nested.");
"Failed to compile '${type.reflectedType}'\n\t-> 'Configuration' subclasses MUST declare an unnammed constructor (i.e. '${type.reflectedType}();') if they are nested.");
}
}
}
Expand Down Expand Up @@ -57,15 +57,16 @@ class MirrorTypeCodec {

Configuration _decodeConfig(dynamic object) {
final item = (type as ClassMirror)
.newInstance(const Symbol(""), []).reflectee as Configuration;
.newInstance(const Symbol(""), []).reflectee as Configuration;

item.decode(object);

return item;
}

List<dynamic> _decodeList(List value) {
final out = (type as ClassMirror).newInstance(const Symbol(''), []).reflectee as List;
final out = (type as ClassMirror)
.newInstance(const Symbol(''), []).reflectee as List;
final innerDecoder = MirrorTypeCodec(type.typeArguments.first);
for (var i = 0; i < value.length; i++) {
try {
Expand All @@ -83,7 +84,7 @@ class MirrorTypeCodec {

Map<dynamic, dynamic> _decodeMap(Map value) {
final map = (type as ClassMirror)
.newInstance(const Symbol(""), []).reflectee as Map;
.newInstance(const Symbol(""), []).reflectee as Map;

final innerDecoder = MirrorTypeCodec(type.typeArguments.last);
value.forEach((key, val) {
Expand Down Expand Up @@ -173,15 +174,13 @@ return map;
}

String get _decodeConfigSource {

return """
final item = ${expectedType}();

item.decode(v);

return item;
""";

}

String get _decodeIntSource {
Expand All @@ -206,7 +205,8 @@ return map;
}

class MirrorConfigurationProperty {
MirrorConfigurationProperty(this.property) : codec = MirrorTypeCodec(property.type);
MirrorConfigurationProperty(this.property)
: codec = MirrorTypeCodec(property.type);

final VariableMirror property;
final MirrorTypeCodec codec;
Expand All @@ -217,15 +217,16 @@ class MirrorConfigurationProperty {
String get source => codec.source;

static bool _isVariableRequired(VariableMirror m) {
final attribute = m.metadata
.firstWhere(
(im) =>
im.type.isSubtypeOf(reflectType(ConfigurationItemAttribute)),
orElse: () => null)
?.reflectee as ConfigurationItemAttribute;

return attribute == null ||
attribute.type == ConfigurationItemAttributeType.required;
try {
final attribute = m.metadata
.firstWhere((im) =>
im.type.isSubtypeOf(reflectType(ConfigurationItemAttribute)))
.reflectee as ConfigurationItemAttribute;

return attribute.type == ConfigurationItemAttributeType.required;
} catch (_) {
return true;
}
}

dynamic decode(dynamic input) {
Expand Down
12 changes: 6 additions & 6 deletions lib/src/runtime.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:mirrors';
import 'package:runtime/runtime.dart';
import 'package:safe_config/src/configuration.dart';

import 'mirror_property.dart';
import 'package:safe_config/src/mirror_property.dart';

class ConfigurationRuntimeImpl extends ConfigurationRuntime
implements SourceCompiler {
Expand All @@ -13,7 +13,7 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime

final ClassMirror type;

Map<String, MirrorConfigurationProperty> properties;
late Map<String, MirrorConfigurationProperty> properties;

@override
void decode(Configuration configuration, Map input) {
Expand Down Expand Up @@ -48,7 +48,8 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime
buf.writeln("final valuesCopy = Map.from(input);");
properties.forEach((k, v) {
buf.writeln("{");
buf.writeln("final v = Configuration.getEnvironmentOrValue(valuesCopy.remove('$k'));");
buf.writeln(
"final v = Configuration.getEnvironmentOrValue(valuesCopy.remove('$k'));");
buf.writeln("if (v != null) {");
buf.writeln(
" final decodedValue = tryDecode(configuration, '$k', () { ${v.source} });");
Expand Down Expand Up @@ -94,7 +95,7 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime
declarations.addAll(ptr.declarations.values
.whereType<VariableMirror>()
.where((vm) => !vm.isStatic && !vm.isPrivate));
ptr = ptr.superclass;
ptr = ptr.superclass!;
}

final m = <String, MirrorConfigurationProperty>{};
Expand Down Expand Up @@ -128,10 +129,9 @@ class ConfigurationRuntimeImpl extends ConfigurationRuntime
@override
String compile(BuildContext ctx) {
final directives = ctx.getImportDirectives(
uri: type.originalDeclaration.location.sourceUri,
uri: type.originalDeclaration.location!.sourceUri,
alsoImportOriginalFile: true)
..add("import 'package:safe_config/src/intermediate_exception.dart';");

return """${directives.join("\n")}
final instance = ConfigurationRuntimeImpl();
class ConfigurationRuntimeImpl extends ConfigurationRuntime {
Expand Down
9 changes: 5 additions & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ homepage: http://stablekernel.com
documentation:

environment:
sdk: ">=2.0.0 <3.0.0"
sdk: ">=2.12.0-0 <3.0.0"

dependencies:
yaml: '>=2.1.8 <3.0.0'
runtime: ^1.0.0-5
yaml: ^3.1.0
runtime:
git: https://github.com/j4qfrost/dart-runtime

dev_dependencies:
test: ^1.3.0
test: ^1.16.5
Loading