abstract base class ViewModel<State, Event> {
ViewModel(Result<State, Exception> state) {
if (state is Ok) {
_state.add(state);
} else {
_state.addError(state);
}
}
final _state = BehaviorSubject<Result<State, Exception>>();
final _event = BehaviorSubject<Event>();
void emit(Result<State, Exception> state) {
if (state is Ok) {
_state.add(state);
} else {
_state.addError(state);
}
}
void add(Event event) {
_event.add(event);
}
Future<void> dispose() async {
await Future.wait(<Future<void>>[
_event.close(),
_state.close(),
]);
}
Stream<Result<State, Exception>> get stream => _state.stream;
Stream<Event> get event => _event.stream;
State get state => _state.value.unwrap;
}
extension type const Option<T>._(T? _) {
bool get isNone => runtimeType == Null && true;
T get unwrap {
if (!isNone) {
return _!;
}
throw NullException();
}
T unwrapOr(T fallback) {
if (!isNone) {
return _!;
}
return fallback;
}
T unwrapOrElse(Never Function() handleNone) => switch (this) {
Some<T>() => unwrap,
None() => handleNone(),
};
R match<R>({
required R Function(T ok) some,
required Never Function() none,
}) =>
switch (this) {
Some<T>(:final T _value) => some(_value),
None() => none(),
};
}
extension type const Some<T>._(T _value) implements Option<T> {
const Some(T value) : this._(value);
}
extension type const None._(Null _) implements Option<Never> {
const None() : this._(null);
}
sealed class Result<T, E extends Exception> {
const Result._();
bool get isErr => this is Err<Exception>;
R bind<R>() {
return this is Ok<T>
? (this as Ok<T>)._value as R
: (this as Err<Exception>)._value as R;
}
}
final class Ok<T> extends Result<T, Never> {
const Ok(T value)
: _value = value,
super._();
final T _value;
}
final class Err<E extends Exception> extends Result<Never, E> {
const Err(E value)
: _value = value,
super._();
final E _value;
}
extension ResultMethods<T> on Result<T, Exception> {
T get unwrap => switch (this) {
Ok<T>(:final T _value) => _value,
Err<Exception>(:final Exception _value) => throw _value,
_ => throw Unreachable(),
};
T unwrapOr(T fallback) => switch (this) {
Ok<T>(:final T _value) => _value,
Err<Exception>() => fallback,
_ => throw Unreachable()
};
T unwrapOrElse(T Function(Exception e) handleErr) => switch (this) {
Ok<T>(:final T _value) => _value,
Err<Exception>(:final Exception _value) => handleErr(_value),
_ => throw Unreachable(),
};
R match<R>({
required R Function(T ok) ok,
required R Function(Exception e) err,
}) =>
switch (this) {
Ok<T>(:final T _value) => ok(_value),
Err<Exception>(:final Exception _value) => err(_value),
_ => throw Unreachable(),
};
}
extension type const FromException(Exception _e) {
Type get type => _e.runtimeType;
String get message => _e.toString().split(':').last.trimLeft();
}
extension type const FromErr(Result<Object, Exception> _e) {
Option<Type> get type {
if (_e case Err<Exception>(:final Exception _value)) {
return Some<Type>(FromException(_value).type);
}
return const None();
}
Option<String> get message {
if (_e case Err<Exception>(:final Exception _value)) {
return Some<String>(FromException(_value).message);
}
return const None();
}
}
extension type const TrySync<T, E extends Exception>(T Function() _) {
Result<T, E> get _wrap {
try {
return Ok<T>(_());
} on Exception catch (e) {
return Err<E>(e as E);
}
}
T get unwrap => _wrap.unwrap;
T unwrapOr(T fallback) => _wrap.unwrapOr(fallback);
}
extension type const TryFuture<T, E extends Exception>(Future<T> Function() _) {
Future<Result<T, E>> get _wrap async {
try {
return Ok<T>(await _());
} on Exception catch (e) {
return Err<E>(e as E);
}
}
Future<T> get unwrap async => (await _wrap).unwrap;
Future<T> unwrapOr(T fallback) async => (await _wrap).unwrapOr(fallback);
}
extension type const TryFutures<T, E extends Exception>(Iterable<Future<T>> _) {
Future<Result<Iterable<T>, E>> _wrap() async {
Future<Iterable<T>> resolver(Iterable<Future<T>> promises) async {
final resolved = <Future<T>>[];
final results = await Future.wait(
promises.map(
(p) => p.then((value) => true).catchError((error, stk) => false)),
eagerError: false,
);
for (var i = 0; i < results.length; i++) {
if (results[i]) {
resolved.add(promises.elementAt(i));
}
}
return await Future.wait(resolved);
}
try {
return Ok(await resolver(_));
} on Exception catch (e) {
return Err<E>(e as E);
}
}
Future<Iterable<T>> get unwrap async => (await _wrap()).unwrap;
Future<Iterable<T>> unwrapOr(Iterable<T> fallback) async =>
(await _wrap()).unwrapOr(fallback);
}