mirror of
https://github.com/immich-app/immich.git
synced 2025-06-03 21:54:21 -04:00
add option type
This commit is contained in:
parent
a9e7d0388b
commit
a4a3d13f7c
86
mobile/lib/utils/option.dart
Normal file
86
mobile/lib/utils/option.dart
Normal file
@ -0,0 +1,86 @@
|
||||
sealed class Option<T> {
|
||||
const Option();
|
||||
|
||||
bool get isSome => this is Some<T>;
|
||||
bool get isNone => this is None;
|
||||
|
||||
factory Option.some(T value) {
|
||||
return Some<T>(value);
|
||||
}
|
||||
|
||||
factory Option.none() {
|
||||
return const None();
|
||||
}
|
||||
|
||||
factory Option.from(T? value) {
|
||||
if (value == null) {
|
||||
return const None();
|
||||
}
|
||||
return Some<T>(value);
|
||||
}
|
||||
|
||||
T? unwrapOrNull() {
|
||||
if (this is Some<T>) {
|
||||
return (this as Some<T>).value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
T unwrap() {
|
||||
if (this is Some<T>) {
|
||||
return (this as Some<T>).value;
|
||||
}
|
||||
throw StateError('Cannot unwrap None');
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! Option<T>) return false;
|
||||
if (this is None && other is None) {
|
||||
return true;
|
||||
}
|
||||
return this is Some<T> &&
|
||||
other is Some<T> &&
|
||||
this.unwrap() == other.unwrap();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
if (this is Some<T>) {
|
||||
return (this.unwrap()).hashCode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (this is Some<T>) {
|
||||
return 'Some(${this.unwrap()})';
|
||||
}
|
||||
return 'None';
|
||||
}
|
||||
}
|
||||
|
||||
class Some<T> extends Option<T> {
|
||||
final T value;
|
||||
|
||||
const Some(this.value);
|
||||
}
|
||||
|
||||
class None extends Option<Never> {
|
||||
const None();
|
||||
}
|
||||
|
||||
// Implemented as an extension rather than adding to the Option class because
|
||||
// the type argument for None is not known at compile time, and fallback to Never
|
||||
// As such, when the method is called on Option<Never>, with a default value of T,
|
||||
// a runtime error
|
||||
extension OptionExtensions<T> on Option<T> {
|
||||
T unwrapOr(T defaultValue) {
|
||||
if (this is Some<T>) {
|
||||
return (this as Some<T>).value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
93
mobile/test/utils/option_test.dart
Normal file
93
mobile/test/utils/option_test.dart
Normal file
@ -0,0 +1,93 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/utils/option.dart';
|
||||
|
||||
void main() {
|
||||
group('Option', () {
|
||||
test('should create a Some instance', () {
|
||||
const value = 42;
|
||||
final option = Option.some(value);
|
||||
expect(option, isA<Some<int>>());
|
||||
expect(option.isSome, isTrue);
|
||||
expect(option.isNone, isFalse);
|
||||
});
|
||||
|
||||
test('Some instance should hold the correct value', () {
|
||||
const value = 'immich';
|
||||
final option = Option.some(value);
|
||||
expect(option.unwrapOrNull(), equals(value));
|
||||
});
|
||||
});
|
||||
|
||||
test('should create a None instance', () {
|
||||
final option = Option<int>.none();
|
||||
expect(option, isA<None>());
|
||||
expect(option.isSome, isFalse);
|
||||
expect(option.isNone, isTrue);
|
||||
});
|
||||
|
||||
test('None instance are equal', () {
|
||||
final option1 = Option<int>.none();
|
||||
final option2 = Option<int>.none();
|
||||
expect(option1, equals(option2));
|
||||
});
|
||||
|
||||
group('Option.from', () {
|
||||
test('should create a Some instance for a non-null value', () {
|
||||
const value = 100.5;
|
||||
final option = Option.from(value);
|
||||
expect(option, isA<Some<double>>());
|
||||
expect(option.isSome, isTrue);
|
||||
expect(option.isNone, isFalse);
|
||||
expect(option.unwrapOrNull(), equals(value));
|
||||
});
|
||||
|
||||
test('should create a None instance for a null value', () {
|
||||
final String? value = null;
|
||||
final option = Option.from(value);
|
||||
expect(option, isA<None>());
|
||||
expect(option.isSome, isFalse);
|
||||
expect(option.isNone, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('unwrap()', () {
|
||||
test('should return the value for Some', () {
|
||||
const value = 'immich';
|
||||
final option = Option.some(value);
|
||||
expect(option.unwrap(), equals(value));
|
||||
});
|
||||
|
||||
test('should throw StateError for None', () {
|
||||
final option = Option<int>.none();
|
||||
expect(() => option.unwrap(), throwsStateError);
|
||||
});
|
||||
});
|
||||
|
||||
group('unwrapOrNull()', () {
|
||||
test('should return the value for Some', () {
|
||||
const value = 'test';
|
||||
final option = Option.some(value);
|
||||
expect(option.unwrapOrNull(), equals(value));
|
||||
});
|
||||
|
||||
test('should return null for None', () {
|
||||
final option = Option<int>.none();
|
||||
expect(option.unwrapOrNull(), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('unwrapOr()', () {
|
||||
test('should return the value for Some', () {
|
||||
const value = true;
|
||||
const defaultValue = false;
|
||||
final option = Option.some(value);
|
||||
expect(option.unwrapOr(defaultValue), equals(value));
|
||||
});
|
||||
|
||||
test('should return the default value for None', () {
|
||||
const defaultValue = 'immich';
|
||||
final option = Option<String>.none();
|
||||
expect(option.unwrapOr(defaultValue), equals(defaultValue));
|
||||
});
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user