Created
May 22, 2020 05:45
-
-
Save arturaz/51bbc46c381b51e398fa48033de17a00 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:io'; | |
import 'package:functional_data/functional_data.dart'; | |
import 'package:meta/meta.dart'; | |
import 'package:zowo_lib/lib.dart'; | |
part 'Config.g.dart'; | |
@immutable | |
@FunctionalData() | |
class Config extends $Config { | |
final HttpConfig http; | |
final DbConfig db; | |
final CpuConfig cpu; | |
const Config({@required this.http, @required this.db, @required this.cpu}); | |
static final configs = DynamicConfigs.map.flatMapTry((map) => doTry(() => | |
Config( | |
http: map.getAt("http", HttpConfig.configs), | |
db: map.getAt("db", DbConfig.configs), | |
cpu: map.getAt("cpu", CpuConfig.configs) | |
) | |
)); | |
} | |
@immutable | |
@FunctionalData() | |
class HttpConfig extends $HttpConfig { | |
final String host; | |
final int port; | |
const HttpConfig({@required this.host, @required this.port}); | |
static final configs = DynamicConfigs.map.flatMapTry((map) => doTry(() => | |
HttpConfig( | |
host: map.getAt("host", DynamicConfigs.string), | |
port: map.getAt("port", DynamicConfigs.integer) | |
) | |
)); | |
} | |
@immutable | |
@FunctionalData() | |
class DbConfig extends $DbConfig { | |
final File migrationsDir; | |
final DbAuthConfig auth; | |
const DbConfig({@required this.migrationsDir, @required this.auth}); | |
static final configs = DynamicConfigs.map.flatMapTry((map) => doTry(() => | |
DbConfig( | |
migrationsDir: map.getAt("migrationsDir", DynamicConfigs.file), | |
auth: map.getAt("auth", DbAuthConfig.configs) | |
) | |
)); | |
} | |
@immutable | |
@FunctionalData() | |
class DbAuthConfig extends $DbAuthConfig { | |
final String username, password, database; | |
final int port; | |
const DbAuthConfig({@required this.username, @required this.password, @required this.database, @required this.port}); | |
static final configs = DynamicConfigs.map.flatMapTry((map) => doTry(() => | |
DbAuthConfig( | |
username: map.getAt("username", DynamicConfigs.string), | |
password: map.getAt("password", DynamicConfigs.string), | |
database: map.getAt("database", DynamicConfigs.string), | |
port: map.getAt("port", DynamicConfigs.integer) | |
) | |
)); | |
} | |
@immutable | |
@FunctionalData() | |
class CpuConfig extends $CpuConfig { | |
final Option<int> threads; | |
const CpuConfig({@required this.threads}); | |
static final configs = DynamicConfigs.map.flatMapTry((map) => doTry(() => | |
CpuConfig( | |
threads: map.maybeAt("threads", DynamicConfigs.integer) | |
) | |
)); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:io'; | |
import 'package:collection/collection.dart'; | |
import 'package:meta/meta.dart'; | |
import '../functional/Option.dart'; | |
import '../data/collection.dart'; | |
import '../functional/Try.dart'; | |
import '../functional/functions.dart'; | |
import '../functional/Either.dart'; | |
import '../exts/exts.dart'; | |
@immutable | |
class ConfigsError { | |
final SLList<String> path; | |
final String _fromString; | |
final Exception _fromException; | |
const ConfigsError._(this._fromString, this._fromException, this.path); | |
ConfigsError.fromString(String string) | |
: path = SLList<String>.nil(), _fromString = string, _fromException = null; | |
ConfigsError.fromRawException(Exception exception) | |
: path = SLList<String>.nil(), _fromString = null, _fromException = exception; | |
factory ConfigsError.fromException(Exception ex) => | |
ex is ConfigsException ? ex.error : ConfigsError.fromRawException(ex); | |
R iswitch<R>({ | |
@required R Function(String string) fromString, | |
@required R Function(Exception exception) fromException | |
}) => | |
_fromString != null ? fromString(_fromString) : fromException(_fromException); | |
ConfigsError atPath(String s) => ConfigsError._(_fromString, _fromException, s.prepend(path)); | |
@override String toString() => | |
"ConfigsError[at '${path.join(".")}': ${iswitch(fromString: (v) => v, fromException: (e) => e.toString())}]"; | |
} | |
class ConfigsException implements Exception { | |
final ConfigsError error; | |
ConfigsException(this.error); | |
} | |
@immutable | |
abstract class Configs<From, To> { | |
Either<ConfigsError, To> parse(From from); | |
const Configs(); | |
Configs<From, To1> map<To1>(To1 Function(To from) parse) => | |
FuncConfigs((from) => this.parse(from).map((to) => parse(to))); | |
Configs<From, To1> flatMap<To1>(Either<ConfigsError, To1> Function(To from) parse) => | |
FuncConfigs((from) => this.parse(from).flatMap((to) => parse(to))); | |
Configs<From, To1> flatMapTry<To1>(Try<To1> Function(To from) parse) => | |
FuncConfigs((from) => this.parse(from).flatMap((to) => | |
parse(to).fold((a) => Right(a), (exception) => Left(ConfigsError.fromException(exception))) | |
)); | |
} | |
@immutable | |
class FuncConfigs<From, To> extends Configs<From, To> { | |
final Either<ConfigsError, To> Function(From from) parser; | |
const FuncConfigs(this.parser); | |
@override Either<ConfigsError, To> parse(From from) => parser(from); | |
} | |
Configs<dynamic, To> castConfigs<To>() => FuncConfigs<dynamic, To>( | |
(dynamic yaml) => castToEither<To>(yaml).mapLeft((err) => ConfigsError.fromString(err)) | |
); | |
class DynamicConfigs { | |
static final Configs<dynamic, bool> boolean = castConfigs<bool>(); | |
static final Configs<dynamic, String> string = castConfigs<String>(); | |
static final Configs<dynamic, File> file = string.map((from) => File(from)); | |
static final Configs<dynamic, int> integer = castConfigs<int>(); | |
static final Configs<dynamic, double> dbl = castConfigs<double>(); | |
static final Configs<dynamic, List<dynamic>> list = | |
castConfigs<List<dynamic>>().map((from) => DynamicConfigsList(from)); | |
static final Configs<dynamic, DynamicConfigsMap> map = | |
castConfigs<Map<dynamic, dynamic>>().map((from) => DynamicConfigsMap(from)); | |
} | |
class DynamicConfigsList extends DelegatingList<dynamic> { | |
DynamicConfigsList(List<dynamic> base) : super(base); | |
} | |
class DynamicConfigsMap extends DelegatingMap<dynamic, dynamic> { | |
DynamicConfigsMap(Map<dynamic, dynamic> base) : super(base); | |
A getAt<A>(dynamic key, Configs<dynamic, A> configs) { | |
final dynamic maybeDynamic = this[key]; | |
if (maybeDynamic == null) throw ConfigsException(ConfigsError.fromString("Can't find value by key '$key'")); | |
final either = configs.parse(maybeDynamic); | |
if (either.isLeft) throw ConfigsException(either.leftOrThrow.atPath(key.toString())); | |
return either.rightOrThrow; | |
} | |
Option<A> maybeAt<A>(dynamic key, Configs<dynamic, A> configs) { | |
final dynamic maybeDynamic = this[key]; | |
if (maybeDynamic == null) return const Option.none(); | |
final either = configs.parse(maybeDynamic); | |
if (either.isLeft) throw ConfigsException(either.leftOrThrow.atPath(key.toString())); | |
return Some(either.rightOrThrow); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Future<void> main(List<String> inputArgs) async { | |
final parser = args.ArgParser() | |
..addOption("config", abbr: "c", defaultsTo: "server.conf.yaml"); | |
final result = parser.parse(inputArgs); | |
final confFile = | |
Option.check(castTo<String>(result['config'])).toRight(() => "--config option not specified") | |
.flatMap((path) => | |
doTry(() => File(path).readAsStringSync()).toEither.mapLeft((err) => "Can't read file at '$path': $err") | |
) | |
.flatMap<dynamic>((yaml) => | |
doTry<dynamic>(() => loadYaml(yaml)).toEither | |
.mapLeft((err) => "Can't parse config file yaml: $err\n\nYAML:\n$yaml") | |
) | |
.flatMap((dynamic yaml) => Config.configs.parse(yaml).mapLeft((err) => err.toString())); | |
return confFile.fold( | |
(err) { | |
stderr.writeln(err); | |
// 64: command line usage error | |
exit(64); | |
}, | |
(conf) async { | |
final isolates = conf.cpu.threads.fold(() => SysInfo.processors.length, (v) => v); | |
final http = conf.http; | |
stdout.writeln('Launching $isolates isolates on ${http.host}:${http.port}.'); | |
final isolateFutures = | |
List.generate(isolates - 1, (idx) => Isolate.spawn<ShelfArgs>(runShelf, ShelfArgs(idx, conf))); | |
await isolateFutures.sequence(); | |
await runShelf(ShelfArgs(isolates - 1, conf)); | |
stdout.writeln('Launched all isolates on ${http.host}:${http.port}.'); | |
} | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment