Skip to content

Commit

Permalink
Merge pull request #12 from westracer/null_safety
Browse files Browse the repository at this point in the history
Null safety
  • Loading branch information
westracer committed Apr 22, 2021
2 parents dc5c5ff + ebeb82b commit bdcb8ad
Show file tree
Hide file tree
Showing 51 changed files with 668 additions and 575 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,8 @@
## 0.3.0-nullsafety.0

* Migrate to null safety
* Fix unknown config parameters causing a crash and add related warnings

## 0.2.0

* **IMPORTANT:** 'CFF' table is generated now instead of 'CFF2'.
Expand Down
12 changes: 6 additions & 6 deletions bin/fontify.dart
Expand Up @@ -14,7 +14,7 @@ final _argParser = ArgParser(allowTrailingOptions: true);
void main(List<String> args) {
defineOptions(_argParser);

CliArguments parsedArgs;
late final CliArguments parsedArgs;

try {
parsedArgs = parseArgsAndConfig(_argParser, args);
Expand All @@ -29,7 +29,7 @@ void main(List<String> args) {

try {
_run(parsedArgs);
} on dynamic catch (e) {
} on Object catch (e) {
logger.e(e.toString());
exit(65);
}
Expand All @@ -47,11 +47,11 @@ void _run(CliArguments parsedArgs) {

if (parsedArgs.classFile?.existsSync() ?? false) {
logger.v(
'Output file for a Flutter class already exists (${parsedArgs.classFile.path}) - '
'Output file for a Flutter class already exists (${parsedArgs.classFile!.path}) - '
'overwriting it');
}

if (parsedArgs.fontFile?.existsSync() ?? false) {
if (!parsedArgs.fontFile.existsSync()) {
logger.v(
'Output file for a font file already exists (${parsedArgs.fontFile.path}) - '
'overwriting it');
Expand Down Expand Up @@ -96,7 +96,7 @@ void _run(CliArguments parsedArgs) {
package: parsedArgs.fontPackage,
);

parsedArgs.classFile.writeAsStringSync(classString);
parsedArgs.classFile!.writeAsStringSync(classString);
}

logger.i('Generated in ${stopwatch.elapsedMilliseconds}ms');
Expand All @@ -112,7 +112,7 @@ void _usageError(String error) {
exit(64);
}

void _printUsage([String error]) {
void _printUsage([String? error]) {
final message = error ?? _kAbout;

stdout.write('''
Expand Down
143 changes: 84 additions & 59 deletions lib/src/cli/arguments.dart
Expand Up @@ -121,7 +121,7 @@ class CliArguments {
///
/// Throws [CliArgumentException], if there is an error in arg parsing
/// or if argument has wrong type.
factory CliArguments.fromMap(Map<CliArgument, Object> rawArgMap) {
factory CliArguments.fromMap(Map<CliArgument, dynamic> rawArgMap) {
// Validating types
for (final e in _kArgAllowedTypes.entries) {
final arg = e.key;
Expand All @@ -136,58 +136,36 @@ class CliArguments {
}

final map = formatArguments(rawArgMap);
_validate(map);

return CliArguments(
map[CliArgument.svgDir] as Directory,
map[CliArgument.fontFile] as File,
map[CliArgument.classFile] as File,
map[CliArgument.className] as String,
map[CliArgument.indent] as int,
map[CliArgument.fontPackage] as String,
map[CliArgument.fontName] as String,
map[CliArgument.recursive] as bool,
map[CliArgument.ignoreShapes] as bool,
map[CliArgument.normalize] as bool,
map[CliArgument.verbose] as bool,
map[CliArgument.configFile] as File,
map[CliArgument.classFile] as File?,
map[CliArgument.className] as String?,
map[CliArgument.indent] as int?,
map[CliArgument.fontPackage] as String?,
map[CliArgument.fontName] as String?,
map[CliArgument.recursive] as bool?,
map[CliArgument.ignoreShapes] as bool?,
map[CliArgument.normalize] as bool?,
map[CliArgument.verbose] as bool?,
map[CliArgument.configFile] as File?,
);
}

final Directory svgDir;
final File fontFile;
final File classFile;
final String className;
final String fontPackage;
final int indent;
final String fontName;
final bool recursive;
final bool ignoreShapes;
final bool normalize;
final bool verbose;
final File configFile;

/// Validates CLI arguments.
///
/// Throws [CliArgumentException], if argument is not valid.
void validate() {
if (svgDir == null) {
throw CliArgumentException('The input directory is not specified.');
}

if (fontFile == null) {
throw CliArgumentException('The output font file is not specified.');
}

if (svgDir.statSync().type != FileSystemEntityType.directory) {
throw CliArgumentException(
"The input directory is not a directory or it doesn't exist.");
}

if (indent != null && indent < 0) {
throw CliArgumentException(
'indent must be a non-negative integer, was $indent.');
}
}
final File? classFile;
final String? className;
final String? fontPackage;
final int? indent;
final String? fontName;
final bool? recursive;
final bool? ignoreShapes;
final bool? normalize;
final bool? verbose;
final File? configFile;
}

/// Parses argument list.
Expand All @@ -196,7 +174,7 @@ class CliArguments {
///
/// Returns an instance of [CliArguments] containing all parsed data.
CliArguments parseArguments(ArgParser argParser, List<String> args) {
ArgResults argResults;
late final ArgResults argResults;
try {
argResults = argParser.parse(args);
} on FormatException catch (err) {
Expand All @@ -210,7 +188,7 @@ CliArguments parseArguments(ArgParser argParser, List<String> args) {
final posArgsLength =
math.min(_kPositionalArguments.length, argResults.rest.length);

final rawArgMap = <CliArgument, Object>{
final rawArgMap = <CliArgument, dynamic>{
for (final e in kOptionNames.entries) e.key: argResults[e.value],
for (var i = 0; i < posArgsLength; i++)
_kPositionalArguments[i]: argResults.rest[i],
Expand All @@ -219,30 +197,48 @@ CliArguments parseArguments(ArgParser argParser, List<String> args) {
return CliArguments.fromMap(rawArgMap);
}

MapEntry<CliArgument, dynamic>? _mapConfigKeyEntry(
MapEntry<dynamic, dynamic> e,
) {
final dynamic rawKey = e.key;
void logUnknown() => logger.w('Unknown config parameter "$rawKey"');

if (rawKey is! String) {
logUnknown();
return null;
}

final key = kConfigKeys.getKeyForValue(rawKey);
if (key == null) {
logUnknown();
return null;
}

return MapEntry<CliArgument, dynamic>(key, e.value);
}

/// Parses config file.
///
/// Returns an instance of [CliArguments] containing all parsed data or null,
/// if 'fontify' key is not present in config file.
CliArguments parseConfig(String config) {
final Object parsedYaml = loadYaml(config);
CliArguments? parseConfig(String config) {
final dynamic yamlMap = loadYaml(config);

if (parsedYaml is! YamlMap) {
if (yamlMap is! YamlMap) {
return null;
}

final yamlMap = parsedYaml as YamlMap;
final Object fontifyYaml = yamlMap['fontify'];
final dynamic fontifyYamlMap = yamlMap['fontify'];

if (fontifyYaml is! YamlMap) {
if (fontifyYamlMap is! YamlMap) {
return null;
}

final fontifyYamlMap = fontifyYaml as YamlMap;
final entries = fontifyYamlMap.entries
.map(_mapConfigKeyEntry)
.whereType<MapEntry<CliArgument, dynamic>>();

final argMap = <CliArgument, Object>{
for (final e in fontifyYamlMap.entries)
if (e.key is String) kConfigKeys.getKeyForValue(e.key as String): e.value,
};
final argMap = Map<CliArgument, dynamic>.fromEntries(entries);

return CliArguments.fromMap(argMap);
}
Expand All @@ -259,7 +255,7 @@ CliArguments parseArgsAndConfig(ArgParser argParser, List<String> args) {
final configList = [parsedArgs.configFile, ...defaultConfigList];

for (final configFile in configList) {
if (configFile?.existsSync() ?? false) {
if (configFile != null && configFile.existsSync()) {
final parsedConfig = parseConfig(configFile.readAsStringSync());

if (parsedConfig != null) {
Expand All @@ -270,10 +266,36 @@ CliArguments parseArgsAndConfig(ArgParser argParser, List<String> args) {
}
}

parsedArgs.validate();
return parsedArgs;
}

/// Validates CLI arguments.
///
/// Throws [CliArgumentException], if argument is not valid.
void _validate(Map<CliArgument, dynamic> args) {
final svgDir = args[CliArgument.svgDir] as Directory?;
final fontFile = args[CliArgument.fontFile] as File?;
final indent = args[CliArgument.indent] as int?;

if (svgDir == null) {
throw CliArgumentException('The input directory is not specified.');
}

if (fontFile == null) {
throw CliArgumentException('The output font file is not specified.');
}

if (svgDir.statSync().type != FileSystemEntityType.directory) {
throw CliArgumentException(
"The input directory is not a directory or it doesn't exist.");
}

if (indent != null && indent < 0) {
throw CliArgumentException(
'indent must be a non-negative integer, was $indent.');
}
}

class CliArgumentException implements Exception {
CliArgumentException(this.message);

Expand All @@ -284,3 +306,6 @@ class CliArgumentException implements Exception {
}

class CliHelpException implements Exception {}

// Ignoring as CLI arguments are dynamically typed
// ignore_for_file: avoid_annotating_with_dynamic
27 changes: 14 additions & 13 deletions lib/src/cli/formatter.dart
Expand Up @@ -2,7 +2,7 @@ import 'dart:io';

import 'arguments.dart';

typedef CliArgumentFormatter = Object Function(Object arg);
typedef CliArgumentFormatter = dynamic Function(dynamic arg);

const _kArgumentFormatters = <CliArgument, CliArgumentFormatter>{
CliArgument.svgDir: _dir,
Expand All @@ -12,11 +12,11 @@ const _kArgumentFormatters = <CliArgument, CliArgumentFormatter>{
CliArgument.configFile: _file,
};

Directory _dir(Object arg) => arg == null ? null : Directory(arg as String);
Directory _dir(dynamic arg) => Directory(arg as String);

File _file(Object arg) => arg == null ? null : File(arg as String);
File _file(dynamic arg) => File(arg as String);

int _indent(Object arg) {
int _indent(dynamic arg) {
if (arg is int) {
return arg;
}
Expand All @@ -29,13 +29,11 @@ int _indent(Object arg) {
throwException();
}

int indent;
late final int indent;
final indentArg = arg as String;

try {
if (indentArg != null) {
indent = int.parse(indentArg);
}
indent = int.parse(indentArg);
} on FormatException catch (_) {
throwException();
}
Expand All @@ -44,14 +42,17 @@ int _indent(Object arg) {
}

/// Formats arguments.
Map<CliArgument, Object> formatArguments(Map<CliArgument, Object> args) {
return args.map((k, v) {
Map<CliArgument, dynamic> formatArguments(Map<CliArgument, dynamic> args) {
return args.map<CliArgument, dynamic>((k, dynamic v) {
final formatter = _kArgumentFormatters[k];

if (formatter == null) {
return MapEntry(k, v);
if (formatter == null || v == null) {
return MapEntry<CliArgument, dynamic>(k, v);
}

return MapEntry(k, formatter(v));
return MapEntry<CliArgument, dynamic>(k, formatter(v));
});
}

// Ignoring as CLI arguments are dynamically typed
// ignore_for_file: avoid_annotating_with_dynamic

0 comments on commit bdcb8ad

Please sign in to comment.