Skip to content

Commit

Permalink
feat: implement running strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
LeadcodeDev committed Dec 19, 2024
1 parent 7aae1df commit f831be0
Show file tree
Hide file tree
Showing 14 changed files with 235 additions and 138 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# 4.0.0-dev.5
# 4.0.0-dev.6
- Enhance architecture
- Move interfaces to dedicated domain
- Rename mixins
- Change `Dialog` methods builder
- Implement global states
- Migrate services passing in constructors to `ioc` resolver
- Add a reconnection treatment when the heartbeat is missed 3 times
- Implement multiple running strategies

# 4.0.0-dev.5
- Add event parameters
- Prepare integration with `mineral_cli`

Expand Down
147 changes: 68 additions & 79 deletions lib/src/domains/commons/kernel.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import 'dart:io';
import 'dart:isolate';

import 'package:mansion/mansion.dart';
import 'package:mineral/contracts.dart';
import 'package:mineral/src/domains/contracts/wss/running_strategy.dart';
import 'package:mineral/src/domains/events/event_listener.dart';
import 'package:mineral/src/domains/global_states/global_state_manager.dart';
import 'package:mineral/src/domains/providers/provider_manager.dart';
import 'package:mineral/src/infrastructure/internals/environment/app_env.dart';
import 'package:mineral/src/infrastructure/internals/hmr/hot_module_reloading.dart';
import 'package:mineral/src/infrastructure/internals/hmr/watcher_config.dart';
import 'package:mineral/src/infrastructure/internals/wss/running_strategies/default_running_strategy.dart';
import 'package:mineral/src/infrastructure/internals/wss/running_strategies/hmr_running_strategy.dart';
import 'package:mineral/src/infrastructure/internals/wss/shard.dart';
import 'package:mineral/src/infrastructure/internals/wss/starting_strategy.dart';
import 'package:mineral/src/infrastructure/io/exceptions/token_exception.dart';
import 'package:mineral/src/infrastructure/services/http/header.dart';
import 'package:mineral/src/infrastructure/services/http/http_client.dart';
import 'package:mineral/src/infrastructure/services/logger/logger.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';

abstract interface class KernelContract {
Map<int, Shard> get shards;
Expand All @@ -35,9 +32,9 @@ abstract interface class KernelContract {

ProviderManagerContract get providerManager;

HotModuleReloading? get hmr;
HmrContract? get hmr;

DispatchStrategy get dispatchStrategy;
RunningStrategy get runningStrategy;

GlobalStateManagerContract get globalState;

Expand Down Expand Up @@ -78,10 +75,10 @@ final class Kernel implements KernelContract {
final ProviderManagerContract providerManager;

@override
HotModuleReloading? hmr;
HmrContract? hmr;

@override
late final DispatchStrategy dispatchStrategy;
late final RunningStrategy runningStrategy;

@override
final GlobalStateManagerContract globalState;
Expand All @@ -108,8 +105,7 @@ final class Kernel implements KernelContract {
Future<Map<String, dynamic>> getWebsocketEndpoint() async {
final response = await httpClient.get('/gateway/bot');
return switch (response.statusCode) {
int() when httpClient.status.isSuccess(response.statusCode) =>
response.body,
int() when httpClient.status.isSuccess(response.statusCode) => response.body,
int() when httpClient.status.isError(response.statusCode) =>
throw TokenException('This token is invalid or expired'),
_ => throw (response.bodyString),
Expand All @@ -126,92 +122,85 @@ final class Kernel implements KernelContract {
}

if (Isolate.current.debugName == 'main') {
final packageFile =
File(path.join(Directory.current.path, 'pubspec.yaml'));

final packageFileContent = await packageFile.readAsString();
final package = loadYaml(packageFileContent);

final coreVersion = package['dependencies']['mineral'];

_watch.stop();
// final packageFile = File(path.join(Directory.current.path, 'pubspec.yaml'));
//
// final packageFileContent = await packageFile.readAsString();
// final package = loadYaml(packageFileContent);
//
// final coreVersion = package['dependencies']['mineral'];

if (useHmr) {
List<Sequence> buildSubtitle(String key, String value) {
return [
const CursorPosition.moveRight(2),
SetStyles(Style.foreground(Logger.primaryColor)),
Print('➜ '),
SetStyles(Style.foreground(Color.white), Style.bold),
Print('$key: '),
SetStyles.reset,
Print(value),
];
}

stdout
..writeAnsiAll([
CursorPosition.reset,
Clear.all,
AsciiControl.lineFeed,
const CursorPosition.moveRight(2),
SetStyles(Style.foreground(Logger.primaryColor), Style.bold),
Print('Mineral v4.0.0-dev.1'),
SetStyles.reset,
const CursorPosition.moveRight(2),
SetStyles(Style.foreground(Logger.mutedColor)),
Print('ready in '),
SetStyles(Style.foreground(Color.white)),
Print('${_watch.elapsedMilliseconds} ms'),
SetStyles.reset,
AsciiControl.lineFeed,
AsciiControl.lineFeed,
])
..writeAnsiAll([
...buildSubtitle('Github', 'https://github.com/mineral-dart'),
AsciiControl.lineFeed,
...buildSubtitle('Docs', 'https://mineral-foundation.org'),
SetStyles.reset,
AsciiControl.lineFeed,
AsciiControl.lineFeed,
]);
// List<Sequence> buildSubtitle(String key, String value) {
// return [
// const CursorPosition.moveRight(2),
// SetStyles(Style.foreground(Logger.primaryColor)),
// Print('➜ '),
// SetStyles(Style.foreground(Color.white), Style.bold),
// Print('$key: '),
// SetStyles.reset,
// Print(value),
// ];
// }
//
// stdout
// ..writeAnsiAll([
// CursorPosition.reset,
// Clear.all,
// AsciiControl.lineFeed,
// const CursorPosition.moveRight(2),
// SetStyles(Style.foreground(Logger.primaryColor), Style.bold),
// Print('Mineral v4.0.0-dev.1'),
// SetStyles.reset,
// const CursorPosition.moveRight(2),
// SetStyles(Style.foreground(Logger.mutedColor)),
// Print('ready in '),
// SetStyles(Style.foreground(Color.white)),
// Print('${_watch.elapsedMilliseconds} ms'),
// SetStyles.reset,
// AsciiControl.lineFeed,
// AsciiControl.lineFeed,
// ])
// ..writeAnsiAll([
// ...buildSubtitle('Github', 'https://github.com/mineral-dart'),
// AsciiControl.lineFeed,
// ...buildSubtitle('Docs', 'https://mineral-foundation.org'),
// SetStyles.reset,
// AsciiControl.lineFeed,
// AsciiControl.lineFeed,
// ]);
} else {
stdout.writeAnsiAll([
CursorPosition.reset,
Clear.all,
AsciiControl.lineFeed,
SetStyles(Style.foreground(Logger.primaryColor), Style.bold),
Print('Mineral v$coreVersion'),
SetStyles.reset,
AsciiControl.lineFeed,
]);
// stdout.writeAnsiAll([
// CursorPosition.reset,
// Clear.all,
// AsciiControl.lineFeed,
// SetStyles(Style.foreground(Logger.primaryColor), Style.bold),
// Print('Mineral v$coreVersion'),
// SetStyles.reset,
// AsciiControl.lineFeed,
// ]);
// '${lightBlue.wrap('mineral v$coreVersion')} ${green.wrap('is running for production…')}');
}
}

if (useHmr) {
final hmr = HotModuleReloading(
_devPort, watcherConfig, this, createShards, shards);
dispatchStrategy = DispatchHmrStrategy(hmr);
this.hmr = hmr;

await hmr.spawn();
hmr = HotModuleReloading(_devPort, watcherConfig, this, createShards, shards);
runningStrategy = HmrRunningStrategy(_watch, hmr!);
} else {
dispatchStrategy = DispatchDefaultStrategy(this);
createShards();
runningStrategy = DefaultRunningStrategy(this, createShards);
}

await runningStrategy.init();
}

Future<void> createShards() async {
final {'url': String endpoint, 'shards': int shardCount} =
await getWebsocketEndpoint();
final {'url': String endpoint, 'shards': int shardCount} = await getWebsocketEndpoint();

for (int i = 0; i < (config.shardCount ?? shardCount); i++) {
final shard = Shard(
shardName: 'shard #$i',
url: '$endpoint/?v=${config.version}',
kernel: this,
dispatchStrategy: dispatchStrategy);
strategy: runningStrategy);

shards.putIfAbsent(i, () => shard);

Expand Down
1 change: 1 addition & 0 deletions lib/src/domains/contracts/hmr/hmr.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
abstract interface class HmrContract {
void send(Map payload);
Future<void> spawn();
}
3 changes: 3 additions & 0 deletions lib/src/domains/contracts/scaffolding/scaffold.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'dart:io';

import 'package:mineral/container.dart';

abstract interface class ScaffoldContract {
File get entrypoint;
Directory get rootDir;
Directory get libDir;
Directory? get binDir;
Directory? get configDir;
IocContainer get container;
}
8 changes: 8 additions & 0 deletions lib/src/domains/contracts/wss/running_strategy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'dart:async';

import 'package:mineral/src/infrastructure/services/wss/websocket_message.dart';

abstract interface class RunningStrategy {
FutureOr<void> init();
FutureOr<void> dispatch(WebsocketMessage message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ final class HotModuleReloading implements HmrContract {
HotModuleReloading(this._devPort, this._watcherConfig, this._kernel,
this._createShards, this._shards);

@override
void send(Map payload) {
devSendPort?.send(payload);
}

@override
Future<void> spawn() async {
if (Isolate.current.debugName == 'dev') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import 'dart:convert';

import 'package:mineral/src/api/common/bot.dart';
import 'package:mineral/src/domains/commands/command_interaction_manager.dart';
import 'package:mineral/src/domains/contracts/logger/logger_contract.dart';
import 'package:mineral/src/domains/events/event.dart';
import 'package:mineral/src/domains/services/container/ioc_container.dart';
import 'package:mineral/src/infrastructure/internals/packets/listenable_packet.dart';
Expand All @@ -15,15 +12,11 @@ final class ReadyPacket implements ListenablePacket {

bool isAlreadyUsed = false;

LoggerContract get _logger => ioc.resolve<LoggerContract>();

@override
Future<void> listen(ShardMessage message, DispatchEvent dispatch) async {
final bot = ioc.make<Bot>(() => Bot.fromJson(message.payload));
final interactionManager = ioc.resolve<CommandInteractionManagerContract>();

_logger.trace(jsonEncode(message.payload));

if (!isAlreadyUsed) {
await interactionManager.registerGlobal(bot);
isAlreadyUsed = true;
Expand Down
4 changes: 4 additions & 0 deletions lib/src/infrastructure/internals/scaffolding/scaffold.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:mineral/contracts.dart';
import 'package:mineral/src/domains/services/container/ioc_container.dart';
import 'package:path/path.dart';

final class DefaultScaffold implements ScaffoldContract {
Expand All @@ -21,4 +22,7 @@ final class DefaultScaffold implements ScaffoldContract {

@override
Directory? get configDir => Directory(join(rootDir.path, 'config'));

@override
IocContainer get container => ioc;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import 'package:mineral/contracts.dart';
import 'package:mineral/src/infrastructure/internals/hmr/hot_module_reloading.dart';
import 'package:mineral/src/domains/contracts/wss/running_strategy.dart';
import 'package:mineral/src/infrastructure/internals/packets/packet_type.dart';
import 'package:mineral/src/infrastructure/internals/wss/shard.dart';
import 'package:mineral/src/infrastructure/internals/wss/shard_message.dart';
import 'package:mineral/src/infrastructure/internals/wss/starting_strategy.dart';
import 'package:mineral/src/infrastructure/services/wss/websocket_message.dart';

final class ShardData implements ShardDataContract {
final DispatchStrategy _dispatchStrategy;
final HotModuleReloading? hmr;
final RunningStrategy _dispatchStrategy;
final HmrContract? hmr;
final Shard _shard;

ShardData(Shard shard, this._dispatchStrategy)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:async';
import 'dart:io';
import 'dart:isolate';

import 'package:mansion/mansion.dart';
import 'package:mineral/services.dart' as services;
import 'package:mineral/src/domains/commons/kernel.dart';
import 'package:mineral/src/domains/commons/utils/file.dart';
import 'package:mineral/src/domains/contracts/wss/running_strategy.dart';
import 'package:mineral/src/infrastructure/services/wss/websocket_message.dart';
import 'package:path/path.dart' as path;

final class DefaultRunningStrategy implements RunningStrategy {
final KernelContract kernel;
final FutureOr<void> Function() onStartup;

DefaultRunningStrategy(this.kernel, this.onStartup) {
kernel.logger.trace('Default strategy initialized');
}

@override
Future<void> init() async {
if (Isolate.current.debugName == 'main') {
final packageFile = File(path.join(Directory.current.path, 'pubspec.yaml'));
final package = await packageFile.readAsYaml();

final coreVersion = package['dependencies']['mineral'];

stdout.writeAnsiAll([
CursorPosition.reset,
Clear.all,
AsciiControl.lineFeed,
SetStyles(Style.foreground(services.Logger.primaryColor), Style.bold),
Print('Mineral v$coreVersion'),
SetStyles.reset,
AsciiControl.lineFeed,
]);
}

onStartup();
}

@override
void dispatch(WebsocketMessage payload) {
kernel.packetListener.dispatcher.dispatch(payload.content);
}
}
Loading

0 comments on commit f831be0

Please sign in to comment.