mirror of
https://github.com/immich-app/immich.git
synced 2025-06-11 01:24:32 -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