Skip to content

Instantly share code, notes, and snippets.

@Davenchy
Created November 1, 2021 12:02
Show Gist options
  • Save Davenchy/3ef75bc15cdedcd0eb621e2e15e9dc91 to your computer and use it in GitHub Desktop.
Save Davenchy/3ef75bc15cdedcd0eb621e2e15e9dc91 to your computer and use it in GitHub Desktop.
Dart Middleware pattern
import 'middleware.dart';
void main(List<String> arguments) {
final password = MiddlewareManager<String, String>();
password.use((context, next, fail) {
if (context.isEmpty) fail('password is required');
next(context);
});
password.use((context, next, fail) {
if (context.length < 8) fail('password minimum 8 characters');
next(context);
});
stdout.write('Enter password: ');
final String pass = stdin.readLineSync() ?? '';
password.execute(
pass,
complete: (context) => print('Strong Password'),
fail: (reason) => print('Bad Password!, Reason: $reason'),
);
}
import 'dart:async';
import 'dart:io';
typedef MiddlewareNextFn<T> = void Function(T context);
typedef MiddlewareFailFn<F> = void Function(F reason);
typedef Middleware<T, F> = FutureOr<void> Function(
T context,
MiddlewareNextFn<T> next,
MiddlewareFailFn<F> fail,
);
typedef HandlerCanceler = Future<void> Function();
class MiddlewareManager<T, F> {
final Set<Middleware<T, F>> _steps = {};
final StreamController<T> _onCompleteEvent = StreamController.broadcast();
final StreamController<F> _onFailEvent = StreamController.broadcast();
int _currentStepIndex = -1;
T? _currentContext;
void use(Middleware<T, F> middleware) => _steps.add(middleware);
void onComplete(MiddlewareNextFn<T> completeHandler) =>
_onCompleteEvent.stream.listen((context) => completeHandler(context));
void onFail(MiddlewareFailFn<F> failHandler) =>
_onFailEvent.stream.listen((reason) => failHandler(reason));
void execute(
T context, {
MiddlewareNextFn<T>? complete,
MiddlewareFailFn<F>? fail,
}) {
if (_steps.isEmpty) return;
_currentContext = context;
if (complete != null) onComplete(complete);
if (fail != null) onFail(fail);
_callStep(0);
}
void _callStep(int index) async {
final step = _steps.elementAt(index);
_currentStepIndex = index;
next(T context) => _nextStep(context, index);
fail(F reason) => _fail(reason, index);
await step(_currentContext!, next, fail);
}
void _nextStep(T context, int stepIndex) {
if (stepIndex != _currentStepIndex) return;
_currentContext = context;
if (stepIndex == _steps.length - 1) {
_onCompleteEvent.add(context);
} else {
_callStep(++stepIndex);
}
}
void _fail(F reason, int stepIndex) {
if (stepIndex != _currentStepIndex) return;
_currentStepIndex = -1;
_onFailEvent.add(reason);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment