Skip to content

Commit

Permalink
✨ implement proper logging
Browse files Browse the repository at this point in the history
Signed-off-by: birjuvachhani <[email protected]>
  • Loading branch information
BirjuVachhani committed Feb 15, 2020
1 parent e6c4405 commit 1805949
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 98 deletions.
43 changes: 20 additions & 23 deletions bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,27 @@
import 'dart:io';

import 'package:args/args.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:spider/spider.dart';
import 'package:spider/src/help_manuals.dart';
import 'package:spider/src/utils.dart';
import 'package:spider/src/version.dart';
import 'package:yaml/yaml.dart';

/// Handles all the commands
void main(List<String> arguments) {
Logger.root.level = Level.INFO; // defaults to Level.INFO
Logger.root.onRecord.listen((record) {
if (record.level == Level.SEVERE) {
stderr.writeln('[${record.level.name}] ${record.message}');
} else {
stdout.writeln('[${record.level.name}] ${record.message}');
}
});
var pubspec_path = path.join(Directory.current.path, 'pubspec.yaml');
if (!File(pubspec_path).existsSync()) {
stderr.writeln(
'Current directory is not flutter project.\nPlease execute this command in a flutter project root path.');
exit(0);
exit_with('Current directory is not flutter project.\nPlease execute '
'this command in a flutter project root path.');
}
exitCode = 0;

Expand All @@ -50,7 +58,7 @@ void main(List<String> arguments) {
processCreateCommand(argResults.command);
break;
default:
stderr.writeln(
exit_with(
'No command found. Use Spider --help to see available commands');
}
}
Expand All @@ -72,9 +80,7 @@ void processArgs(List<String> arguments) {

/// prints library info read from pubspec file
void printInfo() {
try {
final yaml = _loadPubspec();
final info = '''
final info = '''
SPIDER:
A small dart command-line tool for generating dart references of assets from
Expand All @@ -86,25 +92,14 @@ SPIDER:
see spider --help for more available commands.
''';
stdout.writeln(info);
} catch (e) {
stderr.writeln('Unable to get info!');
}
stdout.writeln(info);
}

/// prints library version
void printVersion() {
stdout.writeln(packageVersion);
}

Map _loadPubspec() {
final pathToYaml = path.join(
path.dirname(path.dirname(Platform.script.toFilePath())), 'pubspec.yaml');
final pubspecFile = File(pathToYaml);
Map yaml = loadYaml(pubspecFile.readAsStringSync());
return yaml;
}

/// Parses command-line arguments and returns results
ArgResults parseArguments(List<String> arguments) {
final createParser = ArgParser()
Expand All @@ -131,8 +126,9 @@ ArgResults parseArguments(List<String> arguments) {
try {
var result = parser.parse(arguments);
return result;
} catch (e) {
stderr.writeln('Invalid command input. see spider --help for info.');
} on Error catch (e) {
exit_with(
'Invalid command input. see spider --help for info.', e.stackTrace);
return null;
}
}
Expand All @@ -144,8 +140,9 @@ void processBuildCommand(ArgResults command) {
} else {
var watch = command.arguments.contains('--watch');
var verbose = command.arguments.contains('--verbose');
Logger.root.level = verbose ? Level.ALL : Level.INFO;
final spider = Spider(Directory.current.path);
spider.build(watch, verbose: verbose);
spider.build(watch);
}
}

Expand Down
26 changes: 16 additions & 10 deletions lib/spider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,36 @@ import 'src/utils.dart';
/// provides various functions to execute commands
/// Responsible for triggering dart code generation
class Spider {
final String _path;
List<AssetGroup> groups;

Spider(this._path) : groups = parseConfig(_path);
Spider(String path) : groups = parseConfig(path);

/// Triggers build
/// [watch] determines if the directory should be watched for changes
void build(bool watch, {bool verbose = false}) {
void build(
bool watch,
) {
if (groups == null) {
exit_with('No groups found in config file.');
}
for (var group in groups) {
var generator = DartClassGenerator(group: group, verbose: verbose);
var generator = DartClassGenerator(group);
generator.generate(watch);
}
}

/// initializes config file (spider.yaml) in the root of the project
static void createConfigs(bool isJson) {
var ext = isJson ? 'json' : 'yaml';
var src =
path.join(path.dirname(path.dirname(Platform.script.toFilePath())), '/config.$ext');
var dest = 'spider' + path.extension(src);
File(src).copySync(dest);
stdout.writeln('Configuration file created successfully.');
try {
var ext = isJson ? 'json' : 'yaml';
var src = path.join(
path.dirname(path.dirname(Platform.script.toFilePath())),
'/config.$ext');
var dest = 'spider' + path.extension(src);
File(src).copySync(dest);
success('Configuration file created successfully.');
} on Error catch (e) {
exit_with('Unable to create config file', e.stackTrace);
}
}
}
77 changes: 48 additions & 29 deletions lib/src/dart_class_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,58 +28,77 @@ import 'utils.dart';

/// Generates dart class code using given data
class DartClassGenerator {
bool verbose = false;
final AssetGroup group;
bool _processing = false;
static final formatter = DartFormatter();

DartClassGenerator({this.group, this.verbose = false});
DartClassGenerator(this.group);

/// generates dart class code and returns it as a single string
void generate(bool watch) {
if (watch) {
verbose('path ${group.path} is requested to be watched');
_watchDirectory();
}
process();
}

void process() {
var properties = createFileMap();
var properties_strings = properties.keys.map<String>((name) {
printVerbose(verbose, 'processing ${properties[name]}');
var str = group.useStatic ? '\tstatic ' : '\t';
str += group.useConst ? 'const ' : '';
str +=
'String ${Formatter.formatName(name)} = \'${Formatter.formatPath(properties[name])}\';';
return str;
}).toList();
var dart_class = '''// Generated by spider on ${DateTime.now()}
try {
info('Processing path: ${group.path}');
verbose('Creating file map from ${group.path}');
var properties = createFileMap();
verbose('File map created for path ${group.path}');
var properties_strings = properties.keys.map<String>((name) {
verbose('processing ${path.basename(properties[name])}');
var str = group.useStatic ? '\tstatic ' : '\t';
str += group.useConst ? 'const ' : '';
str +=
'String ${Formatter.formatName(name)} = \'${Formatter.formatPath(properties[name])}\';';
return str;
}).toList();
verbose('Constructing dart class for ${group.className}');
var dart_class = '''// Generated by spider on ${DateTime.now()}
class ${group.className} {
${properties_strings.join('\n')}
}''';
writeToFile(
name: Formatter.formatFileName(group.fileName ?? group.className),
path: group.package,
content: formatter.format(dart_class));
_processing = false;
stdout.writeln('Processed items: ${properties.length}');
verbose('Writing class ${group.className} to file ${group.fileName}');
writeToFile(
name: Formatter.formatFileName(group.fileName ?? group.className),
path: group.package,
content: formatter.format(dart_class));
_processing = false;
success(
'Processed items for class ${group.className}: ${properties.length}');
} on Error catch (e) {
exit_with('Unable to process assets', e.stackTrace);
}
}

/// Creates map from files list of a [dir] where key is the file name without
/// extension and value is the path of the file
Map<String, String> createFileMap() {
var dir = group.path;
var files =
Directory(dir).listSync().where((file) => _isValidFile(file)).toList();
try {
var dir = group.path;
var files = Directory(dir).listSync().where((file) {
final valid = _isValidFile(file);
verbose(
'Asset - ${path.basename(file.path)} is ${valid ? 'selected' : 'not selected'}');
return valid;
}).toList();

if (files.isEmpty) {
exit_with('Directory $dir does not contain any assets!');
if (files.isEmpty) {
exit_with('Directory $dir does not contain any assets!');
}
return {
for (var file in files)
path.basenameWithoutExtension(file.path): file.path
};
} on Error catch (e) {
exit_with('Unable to create file map', e.stackTrace);
return null;
}
return {
for (var file in files)
path.basenameWithoutExtension(file.path): file.path
};
}

/// checks whether the file is valid file to be included or not
Expand All @@ -93,9 +112,9 @@ ${properties_strings.join('\n')}

/// Watches assets dir for file changes and rebuilds dart code
void _watchDirectory() {
stdout.writeln('Watching for changes in directory ${group.path}...');
info('Watching for changes in directory ${group.path}...');
Directory(group.path).watch(events: FileSystemEvent.all).listen((data) {
printVerbose(verbose, 'something changed...');
verbose('something changed...');
if (!_processing) {
_processing = true;
Future.delayed(Duration(seconds: 1), () => process());
Expand Down
86 changes: 50 additions & 36 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import 'dart:convert';
import 'dart:io';

import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:yaml/yaml.dart';

Expand All @@ -39,21 +40,15 @@ void writeToFile({String name, String path, String content}) {
}
var classFile = File(p.join(Constants.LIB_FOLDER, path, name));
classFile.writeAsStringSync(content);
}

/// prints logs if the [verbose] flag is true
void printVerbose(bool verbose, String msg) {
if (verbose) {
stdout.writeln(msg);
}
verbose('File ${p.basename(classFile.path)} is written successfully');
}

/// formats file extensions and adds preceding dot(.) if missing
String formatExtension(String ext) => ext.startsWith('.') ? ext : '.' + ext;

/// exits process with a message on command-line
void exit_with(String msg) {
stderr.writeln(msg);
void exit_with(String msg, [StackTrace stackTrace]) {
error(msg, stackTrace);
exitCode = 2;
exit(2);
}
Expand All @@ -72,54 +67,73 @@ List<AssetGroup> parseConfig(String path) {
final jsonFile = file(p.join(path, 'spider.json'));
var map;
if (yamlFile != null) {
verbose('Loading configs from ${p.basename(yamlFile.path)}');
map = yamlToMap(yamlFile.path);
} else if (jsonFile != null) {
verbose('Loading configs from ${p.basename(jsonFile.path)}');
map = json.decode(jsonFile.readAsStringSync());
} else {
exit_with('Config not found. '
'Create one using "spider create" command.');
}
verbose('Validating configs');
validateConfigs(map);
var groups = <AssetGroup>[];
verbose('Creating asset groups');
map['groups']?.forEach((group) {
groups.add(AssetGroup.fromJson(group));
});
return groups;
} catch (e) {
stderr.writeln(e);
exit_with('Unable to parse configs!');
} on Error catch (e) {
exit_with('Unable to parse configs!', e.stackTrace);
return null;
}
}

/// validates the configs of the configuration file
void validateConfigs(Map<String, dynamic> conf) {
final groups = conf['groups'];
if (groups == null) {
exit_with('No groups found in the config file.');
}
if (groups.runtimeType != <dynamic>[].runtimeType) {
exit_with('Groups must be a list of configurations.');
}
for (var group in groups) {
group.forEach((key, value) {
if (value == null) exit_with('$key cannot be null');
});

if (group['path'] == null) {
exit_with('No path provided for one of the groups.');
}

if (File(group['path']).statSync().type != FileSystemEntityType.directory) {
exit_with('Path ${group['path']} must be a directory');
try {
final groups = conf['groups'];
if (groups == null) {
exit_with('No groups found in the config file.');
}

if (!Directory(group['path']).existsSync()) {
exit_with('${group['path']} does not exist');
if (groups.runtimeType != <dynamic>[].runtimeType) {
exit_with('Groups must be a list of configurations.');
}

if (group['class_name'] == null) {
exit_with('No class name provided for one of the groups.');
for (var group in groups) {
group.forEach((key, value) {
if (value == null) exit_with('$key cannot be null');
});

if (group['path'] == null) {
exit_with('No path provided for one of the groups.');
}

if (File(group['path']).statSync().type !=
FileSystemEntityType.directory) {
exit_with('Path ${group['path']} must be a directory');
}

if (!Directory(group['path']).existsSync()) {
exit_with('${group['path']} does not exist');
}

if (group['class_name'] == null) {
exit_with('No class name provided for one of the groups.');
}
}
} on Error catch (e) {
exit_with('Configs Validation failed', e.stackTrace);
}
}

void error(String msg, [StackTrace stackTrace]) =>
Logger('Spider').log(Level('ERROR', 1100), msg, stackTrace);

void info(String msg) => Logger('Spider').info(msg);

void warning(String msg) => Logger('Spider').warning(msg);

void verbose(String msg) => Logger('Spider').log(Level('DEBUG', 600), msg);

void success(String msg) => Logger('Spider').log(Level('SUCCESS', 1050), msg);
Loading

0 comments on commit 1805949

Please sign in to comment.