types/new_package #13
| @ -29,14 +29,51 @@ | |||||||
| 
 | 
 | ||||||
| Type Utils for Dart & Flutter. | Type Utils for Dart & Flutter. | ||||||
| 
 | 
 | ||||||
| ## Features | ## Option\<T> | ||||||
| 
 | 
 | ||||||
| <!-- TODO --> | **Option** is a container object which may or may not contain a non-null value. If a **Value** is present, `isPresent()` will return true and `get()` will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as `orElse()` (return a default value if value not present) and `ifPresent()` (execute a block of code if the value is present). | ||||||
| 
 | 
 | ||||||
| ## Getting started | ```dart | ||||||
|  | final Option<int> rand = Option.ofNullable( | ||||||
|  |   Random().nextBool() ? null : 10, | ||||||
|  | ); | ||||||
| 
 | 
 | ||||||
| <!-- TODO --> | print( | ||||||
|  |   "`rand` is${rand.isNull ? "" : " not"} null, " | ||||||
|  |   "so it's value is ${rand.orElse(15)}", | ||||||
|  | ); | ||||||
| 
 | 
 | ||||||
| ## Usage | // Ok, `orElse` is equivalent to ?? but the Option is  | ||||||
|  | // useful for match pattern and other utils accesses. | ||||||
|  | rand.match((value) { | ||||||
|  |     print('Value is: $value'); | ||||||
|  |   }, () { | ||||||
|  |     print('Rand is null'); | ||||||
|  |   }, | ||||||
|  | ); | ||||||
| 
 | 
 | ||||||
| <!-- TODO --> | print('`rand` contains 10 ? => ${rand.contains(10)}'); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Result\<T, E> | ||||||
|  | 
 | ||||||
|  | **Result** type is coming from functional languages where exceptions are (rightfully) considered a side-effect, and therefore not appropriate to pass domain errors. Mind the difference between different kinds of errors: Some of them belong to domain, others don't.  | ||||||
|  | 
 | ||||||
|  | *E.g. null reference exception or index out of bounds are not related to domain - they rather indicate a defect.*  | ||||||
|  | 
 | ||||||
|  | Either is defined as a generic type with two branches  | ||||||
|  | - success with **T**  | ||||||
|  | - failure with **E**  | ||||||
|  | 
 | ||||||
|  | It can appear in two forms, where it contains an object of **Ok**, or where it contains an object of **Err**. It cannot appear in both states at once, or in none of them. Therefore, if one possesses an **Result** instance, it either contains a successfully produced result, or contains an error object.  | ||||||
|  | 
 | ||||||
|  | ```dart | ||||||
|  | Future<Result<TodoModel, AppError>> requestTodo() async => Result.tryCatchAsync( | ||||||
|  |   () => get('https://jsonplaceholder.typicode.com/todos/1'), | ||||||
|  |   (error) => ServerError('Error while getting todo'), | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | final Result<TodoModel, AppError> todo1 = await requestTodo(); | ||||||
|  | // In Flutter: | ||||||
|  | todo1.either(buildWidgetWithTodo, buildSizedBox); | ||||||
|  | ``` | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ | |||||||
| # You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | ||||||
| # along with this program. If not, see <https://www.gnu.org/licenses/>. | # along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| include: package:wyatt_analysis/analysis_options.yaml | include: package:wyatt_analysis/analysis_options.yaml | ||||||
|  | 
 | ||||||
|  | analyzer: | ||||||
|  |   exclude: "!example/**" | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								packages/wyatt_type_utils/example/lib/error.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								packages/wyatt_type_utils/example/lib/error.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | class AppError { | ||||||
|  |   final String message; | ||||||
|  | 
 | ||||||
|  |   const AppError(this.message); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   // ignore: no_runtimetype_tostring | ||||||
|  |   String toString() => '$runtimeType: $message'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ServerError extends AppError { | ||||||
|  |   ServerError(super.message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ClientError extends AppError { | ||||||
|  |   ClientError(super.message); | ||||||
|  | } | ||||||
| @ -14,8 +14,50 @@ | |||||||
| // You should have received a copy of the GNU General Public License | // You should have received a copy of the GNU General Public License | ||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
| 
 | 
 | ||||||
|  | import 'dart:math'; | ||||||
|  | 
 | ||||||
|  | import 'package:http/http.dart' as http; | ||||||
|  | import 'package:type_utils_example/error.dart'; | ||||||
|  | import 'package:type_utils_example/todo.dart'; | ||||||
| import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | import 'package:wyatt_type_utils/wyatt_type_utils.dart'; | ||||||
| 
 | 
 | ||||||
| void main() { | Future<TodoModel> get(String url) async { | ||||||
|   print(wyatt()); |   final res = await http.get(Uri.parse(url)); | ||||||
|  |   final todo = TodoModel.fromJson(res.body); | ||||||
|  |   return todo; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Future<Result<TodoModel, AppError>> requestTodo() async => Result.tryCatchAsync( | ||||||
|  |       () => get('https://jsonplaceholder.typicode.com/todos/1'), | ||||||
|  |       (error) => ServerError('Error while getting todo'), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | Future<Result<TodoModel, AppError>> requestBadTodo() async => | ||||||
|  |     Result.tryCatchAsync( | ||||||
|  |       () => get('https://jsonplaceholder.typicode.com/todos/123897'), | ||||||
|  |       (error) => ServerError('Error while getting todo'), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | Future<void> main() async { | ||||||
|  |   final Result<TodoModel, AppError> todo1 = await requestTodo(); | ||||||
|  |   todo1.either(print, print); | ||||||
|  | 
 | ||||||
|  |   final Result<TodoModel, AppError> todo2 = await requestBadTodo(); | ||||||
|  |   todo2.either(print, print); | ||||||
|  | 
 | ||||||
|  |   final Option<int> rand = Option.ofNullable(Random().nextBool() ? null : 10); | ||||||
|  | 
 | ||||||
|  |   print( | ||||||
|  |     "`rand` is${rand.isNull ? "" : " not"} null, " | ||||||
|  |     "so it's value is ${rand.orElse(15)}", | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   // Ok, `orElse` is equivalent to ?? but the Option is useful for match pattern | ||||||
|  |   rand.match((value) { | ||||||
|  |     print('Value is: $value'); | ||||||
|  |   }, () { | ||||||
|  |     print('Rand is null'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   print('`rand` contains 10 ? => ${rand.contains(10)}'); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										85
									
								
								packages/wyatt_type_utils/example/lib/todo.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								packages/wyatt_type_utils/example/lib/todo.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: public_member_api_docs, sort_constructors_first,  | ||||||
|  | // ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes | ||||||
|  | 
 | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | class TodoModel { | ||||||
|  |   final int id; | ||||||
|  |   final int userId; | ||||||
|  |   final String title; | ||||||
|  |   final bool completed; | ||||||
|  |   TodoModel({ | ||||||
|  |     required this.id, | ||||||
|  |     required this.userId, | ||||||
|  |     required this.title, | ||||||
|  |     required this.completed, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   TodoModel copyWith({ | ||||||
|  |     int? id, | ||||||
|  |     int? userId, | ||||||
|  |     String? title, | ||||||
|  |     bool? completed, | ||||||
|  |   }) => | ||||||
|  |       TodoModel( | ||||||
|  |         id: id ?? this.id, | ||||||
|  |         userId: userId ?? this.userId, | ||||||
|  |         title: title ?? this.title, | ||||||
|  |         completed: completed ?? this.completed, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toMap() => <String, dynamic>{ | ||||||
|  |         'id': id, | ||||||
|  |         'userId': userId, | ||||||
|  |         'title': title, | ||||||
|  |         'completed': completed, | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |   factory TodoModel.fromMap(Map<String, dynamic> map) => TodoModel( | ||||||
|  |         id: map['id'] as int, | ||||||
|  |         userId: map['userId'] as int, | ||||||
|  |         title: map['title'] as String, | ||||||
|  |         completed: map['completed'] as bool, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   String toJson() => json.encode(toMap()); | ||||||
|  | 
 | ||||||
|  |   factory TodoModel.fromJson(String source) => | ||||||
|  |       TodoModel.fromMap(json.decode(source) as Map<String, dynamic>); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'TodoModel(id: $id, userId: $userId, title: ' | ||||||
|  |       '$title, completed: $completed)'; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(covariant TodoModel other) { | ||||||
|  |     if (identical(this, other)) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return other.id == id && | ||||||
|  |         other.userId == userId && | ||||||
|  |         other.title == title && | ||||||
|  |         other.completed == completed; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |       id.hashCode ^ userId.hashCode ^ title.hashCode ^ completed.hashCode; | ||||||
|  | } | ||||||
| @ -3,7 +3,7 @@ description: A new Flutter project. | |||||||
| 
 | 
 | ||||||
| # The following line prevents the package from being accidentally published to | # The following line prevents the package from being accidentally published to | ||||||
| # pub.dev using `flutter pub publish`. This is preferred for private packages. | # pub.dev using `flutter pub publish`. This is preferred for private packages. | ||||||
| publish_to: 'none' # Remove this line if you wish to publish to pub.dev | publish_to: "none" # Remove this line if you wish to publish to pub.dev | ||||||
| 
 | 
 | ||||||
| # The following defines the version and build number for your application. | # The following defines the version and build number for your application. | ||||||
| # A version number is three numbers separated by dots, like 1.2.43 | # A version number is three numbers separated by dots, like 1.2.43 | ||||||
| @ -27,20 +27,17 @@ environment: | |||||||
| # the latest version available on pub.dev. To see which dependencies have newer | # the latest version available on pub.dev. To see which dependencies have newer | ||||||
| # versions available, run `flutter pub outdated`. | # versions available, run `flutter pub outdated`. | ||||||
| dependencies: | dependencies: | ||||||
|    |   http: ^0.13.4 | ||||||
| 
 | 
 | ||||||
|   wyatt_type_utils: |   wyatt_type_utils: | ||||||
|     path: "../" |     path: "../" | ||||||
| 
 | 
 | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|    |  | ||||||
| 
 |  | ||||||
|   wyatt_analysis: |   wyatt_analysis: | ||||||
|     git: |     git: | ||||||
|       url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages |       url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages | ||||||
|       ref: wyatt_analysis-v2.1.0 |       ref: wyatt_analysis-v2.1.0 | ||||||
|       path: packages/wyatt_analysis |       path: packages/wyatt_analysis | ||||||
| 
 |  | ||||||
| # For information on the generic Dart part of this file, see the | # For information on the generic Dart part of this file, see the | ||||||
| # following page: https://dart.dev/tools/pub/pubspec | # following page: https://dart.dev/tools/pub/pubspec | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										145
									
								
								packages/wyatt_type_utils/lib/src/either_base.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								packages/wyatt_type_utils/lib/src/either_base.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | |||||||
|  | // // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // // Please see the AUTHORS file for details. | ||||||
|  | // // | ||||||
|  | // // This program is free software: you can redistribute it and/or modify | ||||||
|  | // // it under the terms of the GNU General Public License as published by | ||||||
|  | // // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // // any later version. | ||||||
|  | // // | ||||||
|  | // // This program is distributed in the hope that it will be useful, | ||||||
|  | // // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // // GNU General Public License for more details. | ||||||
|  | // // | ||||||
|  | // // You should have received a copy of the GNU General Public License | ||||||
|  | // // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | // // ignore_for_file: avoid_positional_boolean_parameters | ||||||
|  | 
 | ||||||
|  | part 'future_result.dart'; | ||||||
|  | part 'result.dart'; | ||||||
|  | part 'option.dart'; | ||||||
|  | 
 | ||||||
|  | mixin _Left<LeftType, RightType> on _EitherBase<LeftType, RightType> {} | ||||||
|  | 
 | ||||||
|  | mixin _Right<LeftType, RightType> on _EitherBase<LeftType, RightType> {} | ||||||
|  | 
 | ||||||
|  | class _EitherBaseException implements Exception { | ||||||
|  |   final String message; | ||||||
|  |   const _EitherBaseException(this.message); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => '_EitherException: $message'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | abstract class _EitherBase<LeftType, RightType> { | ||||||
|  |   const _EitherBase(); | ||||||
|  | 
 | ||||||
|  |   bool get _isLeft => this is _Left<LeftType, RightType>; | ||||||
|  | 
 | ||||||
|  |   bool get _isRight => this is _Right<LeftType, RightType>; | ||||||
|  | 
 | ||||||
|  |   LeftType? get _left => _fold<LeftType?>( | ||||||
|  |         (value) => value, | ||||||
|  |         (right) => null, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   RightType? get _right => _fold<RightType?>( | ||||||
|  |         (left) => null, | ||||||
|  |         (value) => value, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   U _unwrap<U>() { | ||||||
|  |     if (U == LeftType) { | ||||||
|  |       return _fold<LeftType>( | ||||||
|  |         (value) => value, | ||||||
|  |         (right) => throw const ResultException( | ||||||
|  |           'Illegal use. You should check left value before calling', | ||||||
|  |         ), | ||||||
|  |       ) as U; | ||||||
|  |     } | ||||||
|  |     if (U == RightType) { | ||||||
|  |       return _fold<RightType>( | ||||||
|  |         (left) => throw const ResultException( | ||||||
|  |           'Illegal use. You should check right value before calling', | ||||||
|  |         ), | ||||||
|  |         (value) => value, | ||||||
|  |       ) as U; | ||||||
|  |     } | ||||||
|  |     throw ResultException( | ||||||
|  |       'Illegal use. You should use $LeftType or $RightType type', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<U> _unwrapAsync<U>() { | ||||||
|  |     if (U == LeftType) { | ||||||
|  |       return _foldAsync<LeftType>( | ||||||
|  |         Future.value, | ||||||
|  |         (right) => throw const ResultException( | ||||||
|  |           'Illegal use. You should check left value before calling', | ||||||
|  |         ), | ||||||
|  |       ) as Future<U>; | ||||||
|  |     } | ||||||
|  |     if (U == RightType) { | ||||||
|  |       return _foldAsync<RightType>( | ||||||
|  |         (left) => throw const ResultException( | ||||||
|  |           'Illegal use. You should check right value before calling', | ||||||
|  |         ), | ||||||
|  |         Future.value, | ||||||
|  |       ) as Future<U>; | ||||||
|  |     } | ||||||
|  |     throw ResultException( | ||||||
|  |       'Illegal use. You should use $LeftType or $RightType type', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   U _fold<U>( | ||||||
|  |     U Function(LeftType left) fnL, | ||||||
|  |     U Function(RightType right) fnR, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   Future<U> _foldAsync<U>( | ||||||
|  |     Future<U> Function(LeftType left) fnL, | ||||||
|  |     Future<U> Function(RightType right) fnR, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   _EitherBase<U, RightType> _and<U>( | ||||||
|  |     _EitherBase<U, RightType> res, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   _EitherBase<LeftType, F> _or<F>( | ||||||
|  |     _EitherBase<LeftType, F> res, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   bool _contains<U>(U x); | ||||||
|  | 
 | ||||||
|  |   LeftType _expect(String msg); | ||||||
|  | 
 | ||||||
|  |   RightType _expectErr(String msg); | ||||||
|  | 
 | ||||||
|  |   _EitherBase<U, RightType> _map<U>( | ||||||
|  |     U Function(LeftType left) mapper, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   Future<_EitherBase<U, RightType>> _mapAsync<U>( | ||||||
|  |     Future<U> Function(LeftType left) mapper, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   _EitherBase<LeftType, F> _mapErr<F>( | ||||||
|  |     F Function(RightType right) mapper, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   Future<_EitherBase<LeftType, F>> _mapErrAsync<F>( | ||||||
|  |     Future<F> Function(RightType right) mapper, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   _EitherBase<L, R> _either<L, R>( | ||||||
|  |     L Function(LeftType left) fnL, | ||||||
|  |     R Function(RightType right) fnR, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   Future<_EitherBase<L, R>> _eitherAsync<L, R>( | ||||||
|  |     Future<L> Function(LeftType left) fnL, | ||||||
|  |     Future<R> Function(RightType right) fnR, | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								packages/wyatt_type_utils/lib/src/future_result.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								packages/wyatt_type_utils/lib/src/future_result.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | part of 'either_base.dart'; | ||||||
|  | 
 | ||||||
|  | extension FutureResult<T, E> on Future<Result<T, E>> { | ||||||
|  |   /// Represents the left side of [Result] class. | ||||||
|  |   Future<bool> get isOk => then((result) => result.isOk); | ||||||
|  | 
 | ||||||
|  |   /// Represents the right side of [Result] class. | ||||||
|  |   Future<bool> get isErr => then((result) => result.isErr); | ||||||
|  | 
 | ||||||
|  |   /// Get [U] value, may throw an exception. | ||||||
|  |   Future<U> unwrap<U>() => then((result) => result.unwrap<U>()); | ||||||
|  | 
 | ||||||
|  |   /// Get **async** [U] value, may throw an exception. | ||||||
|  |   /// | ||||||
|  |   /// With nullable, `Future(Future(U)) == Future(U)` | ||||||
|  |   Future<U> unwrapAsync<U>() => then((result) => result.unwrapAsync<U>()); | ||||||
|  | 
 | ||||||
|  |   /// Fold [Ok] and [Err] into the value of one type | ||||||
|  |   Future<U> fold<U>( | ||||||
|  |     U Function(T value) valueTransformer, | ||||||
|  |     U Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       then((result) => result.fold(valueTransformer, errorTransformer)); | ||||||
|  | 
 | ||||||
|  |   /// Fold [Ok] and [Err] **asynchronously** into the value of one type | ||||||
|  |   Future<U> foldAsync<U>( | ||||||
|  |     Future<U> Function(T value) valueTransformer, | ||||||
|  |     Future<U> Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       then((result) => result.foldAsync(valueTransformer, errorTransformer)); | ||||||
|  | 
 | ||||||
|  |   /// Swap [Ok] and [Err] | ||||||
|  |   Future<Result<E, T>> swap() => then((result) => result.swap()); | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Result] is [Ok], otherwise returns | ||||||
|  |   /// the [Err] value of this. | ||||||
|  |   Future<Result<U, E>> and<U>(Result<U, E> res) => | ||||||
|  |       then((result) => result.and(res)); | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Result] is [Err], otherwise returns | ||||||
|  |   /// the [Ok] value of this. | ||||||
|  |   Future<Result<T, F>> or<F>(Result<T, F> res) => | ||||||
|  |       then((result) => result.or(res)); | ||||||
|  | 
 | ||||||
|  |   /// Returns true if the result is an [Ok] or [Err] value containing | ||||||
|  |   /// the given value/error. | ||||||
|  |   Future<bool> contains<U>(U x) => then((result) => result.contains(x)); | ||||||
|  | 
 | ||||||
|  |   /// Returns the contained [Ok] value. Throw [ResultException] on [Err] with | ||||||
|  |   /// its content. | ||||||
|  |   Future<T> expect(String msg) => then((result) => result.expect(msg)); | ||||||
|  | 
 | ||||||
|  |   /// Returns the contained [Err] value. Throw [ResultException] on [Ok] with | ||||||
|  |   /// its content. | ||||||
|  |   Future<E> expectErr(String msg) => then((result) => result.expectErr(msg)); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying a function to a | ||||||
|  |   /// contained [Ok] value, leaving an [Err] value untouched. | ||||||
|  |   Future<Result<U, E>> map<U>(U Function(T value) mapper) => | ||||||
|  |       then((result) => result.map(mapper)); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function | ||||||
|  |   /// to a contained [Ok] value, leaving an [Err] value untouched. | ||||||
|  |   Future<Result<U, E>> mapAsync<U>(Future<U> Function(T value) mapper) => | ||||||
|  |       then((result) => result.mapAsync(mapper)); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<T, F>] by applying a function to a | ||||||
|  |   /// contained [Err] value, leaving an [Ok] value untouched. | ||||||
|  |   Future<Result<T, F>> mapErr<F>(F Function(E error) mapper) => | ||||||
|  |       then((result) => result.mapErr(mapper)); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function | ||||||
|  |   /// to a contained [Err] value, leaving an [Ok] value untouched. | ||||||
|  |   Future<Result<T, F>> mapErrAsync<F>(Future<F> Function(E error) mapper) => | ||||||
|  |       then((result) => result.mapErrAsync(mapper)); | ||||||
|  | 
 | ||||||
|  |   /// Transforms a [Result<T, E>] to [Result<U, F>] by applying functions to | ||||||
|  |   /// contained [Ok] and [Err] values. | ||||||
|  |   Future<Result<U, F>> either<U, F>( | ||||||
|  |     U Function(T value) valueTransformer, | ||||||
|  |     F Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       then((result) => result.either(valueTransformer, errorTransformer)); | ||||||
|  | 
 | ||||||
|  |   /// Transforms a [Result<T, E>] to [Result<U, F>] by applying **async** | ||||||
|  |   /// functions to contained [Ok] and [Err] values. | ||||||
|  |   Future<Result<U, F>> eitherAsync<U, F>( | ||||||
|  |     Future<U> Function(T value) valueTransformer, | ||||||
|  |     Future<F> Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       then((result) => result.eitherAsync(valueTransformer, errorTransformer)); | ||||||
|  | } | ||||||
							
								
								
									
										301
									
								
								packages/wyatt_type_utils/lib/src/option.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								packages/wyatt_type_utils/lib/src/option.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | part of 'either_base.dart'; | ||||||
|  | 
 | ||||||
|  | /// {@template option_exception} | ||||||
|  | /// [OptionException] is sometimes threw by [Option] objects. | ||||||
|  | /// | ||||||
|  | /// ```dart | ||||||
|  | /// throw OptionException('Emergency failure!'); | ||||||
|  | /// ``` | ||||||
|  | /// {@endtemplate} | ||||||
|  | class OptionException extends _EitherBaseException { | ||||||
|  |   /// {@macro result_exception} | ||||||
|  |   const OptionException(super.message); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'OptionException: $message'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// {@template option} | ||||||
|  | /// [Option] is a container object which may or may not contain a non-null  | ||||||
|  | /// value. If a [Value] is present, isPresent() will return true and get()  | ||||||
|  | /// will return the value. | ||||||
|  | ///  | ||||||
|  | /// Additional methods that depend on the presence or absence of  | ||||||
|  | /// a contained value are provided, such as orElse() (return a default value  | ||||||
|  | /// if value not present) and ifPresent() (execute a block of code if the  | ||||||
|  | /// value is present). | ||||||
|  | /// {@endtemplate} | ||||||
|  | abstract class Option<T> extends _EitherBase<T, void> { | ||||||
|  |   /// {@macro option} | ||||||
|  |   const Option._(); | ||||||
|  | 
 | ||||||
|  |   /// Represents the left side of [Option] class. | ||||||
|  |   bool get isPresent => _isLeft; | ||||||
|  | 
 | ||||||
|  |   /// Represents the right side of [Option] class. | ||||||
|  |   bool get isNull => _isRight; | ||||||
|  | 
 | ||||||
|  |   /// Get nullable [Value]. | ||||||
|  |   T? get() => _left; | ||||||
|  | 
 | ||||||
|  |   /// Get [U] value, may throw an exception. | ||||||
|  |   U unwrap<U>() => _unwrap<U>(); | ||||||
|  | 
 | ||||||
|  |   /// Get **async** [U] value, may throw an exception. | ||||||
|  |   Future<U> unwrapAsync<U>() => _unwrapAsync<U>(); | ||||||
|  | 
 | ||||||
|  |   /// Match [Value] and [None]. | ||||||
|  |   void match( | ||||||
|  |     void Function(T value) value, | ||||||
|  |     void Function() none, | ||||||
|  |   ) => | ||||||
|  |       _fold<void>( | ||||||
|  |         (left) => value(left), | ||||||
|  |         (right) => none(), | ||||||
|  |       ); | ||||||
|  |    | ||||||
|  |   /// Match **async** [Value] and [None]. | ||||||
|  |   Future<void> matchAsync( | ||||||
|  |     Future<void> Function(T value) value, | ||||||
|  |     Future<void> Function() none, | ||||||
|  |   ) => | ||||||
|  |       _foldAsync<void>( | ||||||
|  |         (left) => value(left), | ||||||
|  |         (right) => none(), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// If [Value] is present, invoke the specified consumer | ||||||
|  |   /// with the value, otherwise do nothing. | ||||||
|  |   void ifPresent( | ||||||
|  |     void Function(T value) consumer, | ||||||
|  |   ) => | ||||||
|  |       _fold<void>( | ||||||
|  |         (left) => consumer(left), | ||||||
|  |         (right) => {}, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// Return the [Value] if present, otherwise return [other]. | ||||||
|  |   T orElse( | ||||||
|  |     T other, | ||||||
|  |   ) => | ||||||
|  |       _fold<T>( | ||||||
|  |         (left) => left, | ||||||
|  |         (right) => other, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// Return the [Value] if present, otherwise call and return result | ||||||
|  |   /// of [supplier]. | ||||||
|  |   T orElseLazy( | ||||||
|  |     T Function() supplier, | ||||||
|  |   ) => | ||||||
|  |       _fold<T>( | ||||||
|  |         (left) => left, | ||||||
|  |         (right) => supplier(), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Option] is [Value], otherwise returns | ||||||
|  |   /// the [None] value of this. | ||||||
|  |   Option<U> and<U>(Option<U> res) => _and<U>(res) as Option<U>; | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Option] is [None], otherwise returns | ||||||
|  |   /// the [Value] of this. | ||||||
|  |   Option<T> or(Option<T> res) => _or<void>(res) as Option<T>; | ||||||
|  | 
 | ||||||
|  |   /// Returns true if the result is a [Value]. | ||||||
|  |   bool contains<U>(U x) => _contains(x); | ||||||
|  | 
 | ||||||
|  |   /// Returns the contained [Value]. Throw [OptionException] on [None]. | ||||||
|  |   T expect(String msg) => _expect(msg); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Option<T>] to [Option<U>] by applying a function to a | ||||||
|  |   /// contained [Value]. | ||||||
|  |   Option<U> map<U>(U Function(T value) mapper) => _map(mapper) as Option<U>; | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Option<T>] to [Option<U>] by applying an **async** function | ||||||
|  |   /// to a contained [Value]. | ||||||
|  |   Future<Option<U>> mapAsync<U>(Future<U> Function(T value) mapper) async => | ||||||
|  |       await _mapAsync(mapper) as Option<U>; | ||||||
|  | 
 | ||||||
|  |   /// Constructs an [Option] with the specified present non-null [Value]. | ||||||
|  |   static Value<T> of<T>(T value) { | ||||||
|  |     if (value == null) { | ||||||
|  |       throw const OptionException("Value can' be null. Use `ofNullable()`"); | ||||||
|  |     } | ||||||
|  |     return ofNullable(value) as Value<T>; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Constructs an [Option] describing the specified [Value], | ||||||
|  |   /// if non-null, otherwise returns a [None]. | ||||||
|  |   static Option<T> ofNullable<T>(T? value) { | ||||||
|  |     if (value != null) { | ||||||
|  |       return Value(value); | ||||||
|  |     } else { | ||||||
|  |       return None<T>(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Constructs an [Option] with the specified present non-null [Value]. | ||||||
|  |   static Value<T> ofLazy<T>(T Function() value) { | ||||||
|  |     final v = value(); | ||||||
|  |     if (v == null) { | ||||||
|  |       throw const OptionException("Value can' be null. Use `ofNullableLazy()`"); | ||||||
|  |     } | ||||||
|  |     return ofNullable(v) as Value<T>; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Constructs an [Option] describing the specified [Value], | ||||||
|  |   /// if non-null, otherwise returns a [None]. | ||||||
|  |   static Option<T> ofNullableLazy<T>(T? Function() value) { | ||||||
|  |     final v = value(); | ||||||
|  |     if (v != null) { | ||||||
|  |       return Value(v); | ||||||
|  |     } else { | ||||||
|  |       return None<T>(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Value<T> extends Option<T> with _Left<T, void> { | ||||||
|  |   final T value; | ||||||
|  | 
 | ||||||
|  |   /// {@macro ok} | ||||||
|  |   const Value(this.value) : super._(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<U, void> _and<U>(_EitherBase<U, void> res) => res as Option<U>; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool _contains<U>(U x) => value == x; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<L, R> _either<L, R>( | ||||||
|  |     L Function(T left) fnL, | ||||||
|  |     R Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<L, R>> _eitherAsync<L, R>( | ||||||
|  |     Future<L> Function(T left) fnL, | ||||||
|  |     Future<R> Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   T _expect(String msg) => value; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void _expectErr(String msg) => throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   U _fold<U>(U Function(T left) fnL, U Function(void right) fnR) => fnL(value); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<U> _foldAsync<U>( | ||||||
|  |     Future<U> Function(T left) fnL, | ||||||
|  |     Future<U> Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnL(value); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<U, void> _map<U>(U Function(T left) mapper) => | ||||||
|  |       Value(mapper(value)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<U, void>> _mapAsync<U>( | ||||||
|  |     Future<U> Function(T left) mapper, | ||||||
|  |   ) => | ||||||
|  |       mapper(value).then(Value.new); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<T, F> _mapErr<F>(F Function(void right) mapper) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<T, F>> _mapErrAsync<F>( | ||||||
|  |     Future<F> Function(void right) mapper, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<T, F> _or<F>(_EitherBase<T, F> res) => this as _EitherBase<T, F>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class None<T> extends Option<T> with _Right<T, void> { | ||||||
|  |   /// {@macro ok} | ||||||
|  |   const None() : super._(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<U, void> _and<U>(_EitherBase<U, void> res) => this as Option<U>; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool _contains<U>(U x) => false; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<L, R> _either<L, R>( | ||||||
|  |     L Function(T left) fnL, | ||||||
|  |     R Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<L, R>> _eitherAsync<L, R>( | ||||||
|  |     Future<L> Function(T left) fnL, | ||||||
|  |     Future<R> Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   T _expect(String msg) => throw OptionException(msg); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void _expectErr(String msg) => throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   U _fold<U>(U Function(T left) fnL, U Function(void right) fnR) => fnR(null); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<U> _foldAsync<U>( | ||||||
|  |     Future<U> Function(T left) fnL, | ||||||
|  |     Future<U> Function(void right) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnR(null); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<U, void> _map<U>(U Function(T left) mapper) => None<U>(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<U, void>> _mapAsync<U>( | ||||||
|  |     Future<U> Function(T left) mapper, | ||||||
|  |   ) => | ||||||
|  |       Future.value(None<U>()); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<T, F> _mapErr<F>(F Function(void right) mapper) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<_EitherBase<T, F>> _mapErrAsync<F>( | ||||||
|  |     Future<F> Function(void right) mapper, | ||||||
|  |   ) => | ||||||
|  |       throw UnimplementedError(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _EitherBase<T, F> _or<F>(_EitherBase<T, F> res) => res; | ||||||
|  | } | ||||||
							
								
								
									
										327
									
								
								packages/wyatt_type_utils/lib/src/result.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								packages/wyatt_type_utils/lib/src/result.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,327 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: avoid_positional_boolean_parameters | ||||||
|  | 
 | ||||||
|  | part of 'either_base.dart'; | ||||||
|  | 
 | ||||||
|  | /// {@template result_exception} | ||||||
|  | /// [ResultException] is sometimes threw by [Result] objects. | ||||||
|  | /// | ||||||
|  | /// ```dart | ||||||
|  | /// throw ResultException('Emergency failure!'); | ||||||
|  | /// ``` | ||||||
|  | /// {@endtemplate} | ||||||
|  | class ResultException extends _EitherBaseException { | ||||||
|  |   /// {@macro result_exception} | ||||||
|  |   const ResultException(super.message); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'ResultException: $message'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// {@template result} | ||||||
|  | /// [Result] type is coming from functional languages where exceptions are | ||||||
|  | /// (rightfully) considered a side-effect, and therefore not appropriate to | ||||||
|  | /// pass domain errors. Mind the difference between different kinds of errors: | ||||||
|  | /// Some of them belong to domain, others don't. | ||||||
|  | /// | ||||||
|  | /// *E.g. null reference exception or index out of bounds | ||||||
|  | /// are not related to domain - they rather indicate a defect.* | ||||||
|  | /// | ||||||
|  | /// Either is defined as a generic type with two branches | ||||||
|  | /// - success with [T] | ||||||
|  | /// - failure with [E] | ||||||
|  | /// | ||||||
|  | /// It can appear in two forms, where | ||||||
|  | /// it contains an object of [Ok], or where it contains an object | ||||||
|  | /// of [Err]. It cannot appear in both states at once, or in none of them. | ||||||
|  | /// Therefore, if one possesses an [Result] instance, it either contains a | ||||||
|  | /// successfully produced result, or contains an error object. | ||||||
|  | /// {@endtemplate} | ||||||
|  | abstract class Result<T, E> extends _EitherBase<T, E> { | ||||||
|  |   /// {@macro result} | ||||||
|  |   const Result._(); | ||||||
|  | 
 | ||||||
|  |   /// Represents the left side of [Result] class. | ||||||
|  |   bool get isOk => _isLeft; | ||||||
|  | 
 | ||||||
|  |   /// Represents the right side of [Result] class. | ||||||
|  |   bool get isErr => _isRight; | ||||||
|  | 
 | ||||||
|  |   /// Get nullable [Ok] value, and discarding the error, if any. | ||||||
|  |   T? get ok => _left; | ||||||
|  | 
 | ||||||
|  |   /// Get nullable [Err] value, and discarding the success value, if any. | ||||||
|  |   E? get err => _right; | ||||||
|  | 
 | ||||||
|  |   /// Get [U] value, may throw an exception. | ||||||
|  |   U unwrap<U>() => _unwrap<U>(); | ||||||
|  | 
 | ||||||
|  |   /// Get **async** [U] value, may throw an exception. | ||||||
|  |   Future<U> unwrapAsync<U>() => _unwrapAsync<U>(); | ||||||
|  | 
 | ||||||
|  |   /// Fold [Ok] and [Err] into the value of one type | ||||||
|  |   U fold<U>( | ||||||
|  |     U Function(T value) valueTransformer, | ||||||
|  |     U Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       _fold( | ||||||
|  |         (left) => valueTransformer(left), | ||||||
|  |         (right) => errorTransformer(right), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// Fold [Ok] and [Err] **asynchronously** into the value of one type | ||||||
|  |   Future<U> foldAsync<U>( | ||||||
|  |     Future<U> Function(T value) valueTransformer, | ||||||
|  |     Future<U> Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       _foldAsync( | ||||||
|  |         (left) => valueTransformer(left), | ||||||
|  |         (right) => errorTransformer(right), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   /// Swap [Ok] and [Err] | ||||||
|  |   Result<E, T> swap() => fold(Err.new, Ok.new); | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Result] is [Ok], otherwise returns | ||||||
|  |   /// the [Err] value of this. | ||||||
|  |   Result<U, E> and<U>(Result<U, E> res) => _and<U>(res) as Result<U, E>; | ||||||
|  | 
 | ||||||
|  |   /// Returns [res] if the [Result] is [Err], otherwise returns | ||||||
|  |   /// the [Ok] value of this. | ||||||
|  |   Result<T, F> or<F>(Result<T, F> res) => _or<F>(res) as Result<T, F>; | ||||||
|  | 
 | ||||||
|  |   /// Returns true if the result is an [Ok] or [Err] value containing | ||||||
|  |   /// the given value/error. | ||||||
|  |   bool contains<U>(U x) => _contains(x); | ||||||
|  | 
 | ||||||
|  |   /// Returns the contained [Ok] value. Throw [ResultException] on [Err] with | ||||||
|  |   /// its content. | ||||||
|  |   T expect(String msg) => _expect(msg); | ||||||
|  | 
 | ||||||
|  |   /// Returns the contained [Err] value. Throw [ResultException] on [Ok] with | ||||||
|  |   /// its content. | ||||||
|  |   E expectErr(String msg) => _expectErr(msg); | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying a function to a | ||||||
|  |   /// contained [Ok] value, leaving an [Err] value untouched. | ||||||
|  |   Result<U, E> map<U>(U Function(T value) mapper) => | ||||||
|  |       _map(mapper) as Result<U, E>; | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function | ||||||
|  |   /// to a contained [Ok] value, leaving an [Err] value untouched. | ||||||
|  |   Future<Result<U, E>> mapAsync<U>(Future<U> Function(T value) mapper) => | ||||||
|  |       _mapAsync(mapper) as Future<Result<U, E>>; | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<T, F>] by applying a function to a | ||||||
|  |   /// contained [Err] value, leaving an [Ok] value untouched. | ||||||
|  |   Result<T, F> mapErr<F>(F Function(E error) mapper) => | ||||||
|  |       _mapErr(mapper) as Result<T, F>; | ||||||
|  | 
 | ||||||
|  |   /// Maps a [Result<T, E>] to [Result<U, E>] by applying an **async** function | ||||||
|  |   /// to a contained [Err] value, leaving an [Ok] value untouched. | ||||||
|  |   Future<Result<T, F>> mapErrAsync<F>(Future<F> Function(E error) mapper) => | ||||||
|  |       _mapErrAsync(mapper) as Future<Result<T, F>>; | ||||||
|  | 
 | ||||||
|  |   /// Transforms a [Result<T, E>] to [Result<U, F>] by applying functions to | ||||||
|  |   /// contained [Ok] and [Err] values. | ||||||
|  |   Result<U, F> either<U, F>( | ||||||
|  |     U Function(T value) valueTransformer, | ||||||
|  |     F Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       _either<U, F>(valueTransformer, errorTransformer) as Result<U, F>; | ||||||
|  | 
 | ||||||
|  |   /// Transforms a [Result<T, E>] to [Result<U, F>] by applying **async** | ||||||
|  |   /// functions to contained [Ok] and [Err] values. | ||||||
|  |   Future<Result<U, F>> eitherAsync<U, F>( | ||||||
|  |     Future<U> Function(T value) valueTransformer, | ||||||
|  |     Future<F> Function(E error) errorTransformer, | ||||||
|  |   ) => | ||||||
|  |       _eitherAsync<U, F>(valueTransformer, errorTransformer) | ||||||
|  |           as Future<Result<U, F>>; | ||||||
|  | 
 | ||||||
|  |   /// Constructs a new [Result] from a function that might throw | ||||||
|  |   static Result<T, E> tryCatch<T, E, Error extends Object>( | ||||||
|  |     T Function() tryFn, | ||||||
|  |     E Function(Error error) onError, | ||||||
|  |   ) { | ||||||
|  |     try { | ||||||
|  |       return Ok(tryFn()); | ||||||
|  |     } on Error catch (e) { | ||||||
|  |       return Err(onError(e)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Constructs a new [Result] from an **async** function that might throw | ||||||
|  |   static Future<Result<T, E>> tryCatchAsync<T, E, Error extends Object>( | ||||||
|  |     Future<T> Function() tryFn, | ||||||
|  |     E Function(Error error) onError, | ||||||
|  |   ) async { | ||||||
|  |     try { | ||||||
|  |       return Ok(await tryFn()); | ||||||
|  |     } on Error catch (e) { | ||||||
|  |       return Err(onError(e)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// If the condition is satify then return [value] in | ||||||
|  |   /// [Ok] else [error] in [Err] | ||||||
|  |   static Result<T, E> conditional<T, E>( | ||||||
|  |     bool test, | ||||||
|  |     T value, | ||||||
|  |     E error, | ||||||
|  |   ) => | ||||||
|  |       test ? Ok(value) : Err(error); | ||||||
|  | 
 | ||||||
|  |   /// If the condition is satify then return *command* [value] | ||||||
|  |   /// in [Ok] else [error] in [Err] | ||||||
|  |   static Result<T, E> conditionalLazy<T, E>( | ||||||
|  |     bool test, | ||||||
|  |     T Function() value, | ||||||
|  |     E Function() error, | ||||||
|  |   ) => | ||||||
|  |       test ? Ok(value()) : Err(error()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// {@template ok} | ||||||
|  | /// Contains the success value of a [Result] | ||||||
|  | /// | ||||||
|  | /// {@macro result} | ||||||
|  | /// {@endtemplate} | ||||||
|  | class Ok<T, E> extends Result<T, E> with _Left<T, E> { | ||||||
|  |   final T value; | ||||||
|  | 
 | ||||||
|  |   /// {@macro ok} | ||||||
|  |   const Ok(this.value) : super._(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnL(value); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<U> _foldAsync<U>( | ||||||
|  |     Future<U> Function(T left) fnL, | ||||||
|  |     Future<U> Function(E right) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnL(value); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, E> _and<U>(_EitherBase<U, E> res) => res as Result<U, E>; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<T, F> _or<F>(_EitherBase<T, F> res) => this as Result<T, F>; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool _contains<U>(U x) => value == x; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   T _expect(String msg) => value; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   E _expectErr(String msg) => throw ResultException('$msg: $value'); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, E> _map<U>(U Function(T value) mapper) => Ok(mapper(value)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<U, E>> _mapAsync<U>(Future<U> Function(T value) mapper) => | ||||||
|  |       mapper(value).then(Ok.new); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<T, F> _mapErr<F>(F Function(E error) mapper) => Ok(value); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<T, F>> _mapErrAsync<F>(Future<F> Function(E error) mapper) => | ||||||
|  |       Future.value(Ok(value)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, F> _either<U, F>( | ||||||
|  |     U Function(T value) fnL, | ||||||
|  |     F Function(E error) fnR, | ||||||
|  |   ) => | ||||||
|  |       Ok(fnL(value)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<U, F>> _eitherAsync<U, F>( | ||||||
|  |     Future<U> Function(T value) fnL, | ||||||
|  |     Future<F> Function(E error) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnL(value).then(Ok.new); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// {@template err} | ||||||
|  | /// Contains the error value of a [Result] | ||||||
|  | /// | ||||||
|  | /// {@macro result} | ||||||
|  | /// {@endtemplate} | ||||||
|  | class Err<T, E> extends Result<T, E> with _Right<T, E> { | ||||||
|  |   final E error; | ||||||
|  | 
 | ||||||
|  |   /// {@macro err} | ||||||
|  |   const Err(this.error) : super._(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnR(error); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<U> _foldAsync<U>( | ||||||
|  |     Future<U> Function(T left) fnL, | ||||||
|  |     Future<U> Function(E right) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnR(error); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, E> _and<U>(_EitherBase<U, E> res) => this as Result<U, E>; | ||||||
|  |   @override | ||||||
|  |   Result<T, F> _or<F>(_EitherBase<T, F> res) => res as Result<T, F>; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool _contains<U>(U x) => error == x; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   T _expect(String msg) => throw ResultException('$msg: $error'); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   E _expectErr(String msg) => error; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, E> _map<U>(U Function(T value) mapper) => Err(error); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<U, E>> _mapAsync<U>(Future<U> Function(T value) mapper) => | ||||||
|  |       Future.value(Err(error)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<T, F> _mapErr<F>(F Function(E error) mapper) => Err(mapper(error)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<T, F>> _mapErrAsync<F>(Future<F> Function(E error) mapper) => | ||||||
|  |       mapper(error).then(Err.new); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Result<U, F> _either<U, F>( | ||||||
|  |     U Function(T value) fnL, | ||||||
|  |     F Function(E error) fnR, | ||||||
|  |   ) => | ||||||
|  |       Err(fnR(error)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Result<U, F>> _eitherAsync<U, F>( | ||||||
|  |     Future<U> Function(T value) fnL, | ||||||
|  |     Future<F> Function(E error) fnR, | ||||||
|  |   ) => | ||||||
|  |       fnR(error).then(Err.new); | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								packages/wyatt_type_utils/lib/src/src.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/wyatt_type_utils/lib/src/src.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | export 'either_base.dart'; | ||||||
							
								
								
									
										20
									
								
								packages/wyatt_type_utils/lib/wyatt_type_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/wyatt_type_utils/lib/wyatt_type_utils.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | /// Type Utils | ||||||
|  | library wyatt_type_utils; | ||||||
|  | 
 | ||||||
|  | export 'src/src.dart'; | ||||||
| @ -1,5 +1,5 @@ | |||||||
| name: wyatt_type_utils | name: wyatt_type_utils | ||||||
| description: Either, Optional and other useful types. | description: Either, Option and other useful types. | ||||||
| repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_type_utils | repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_type_utils | ||||||
| version: 0.0.1 | version: 0.0.1 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										280
									
								
								packages/wyatt_type_utils/test/option_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								packages/wyatt_type_utils/test/option_test.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,280 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:test/test.dart'; | ||||||
|  | import 'package:wyatt_type_utils/src/either_base.dart'; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   group('Option<T>', () { | ||||||
|  |     test('`isPresent` returns true on not null value', () { | ||||||
|  |       expect( | ||||||
|  |         const Value<int>(10).isPresent, | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         const None<int>().isPresent, | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('`isNull` returns true on null value', () { | ||||||
|  |       expect( | ||||||
|  |         const Value<int>(10).isNull, | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         const None<int>().isNull, | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('`get` returns value', () { | ||||||
|  |       const Option<int> x = Value(2); | ||||||
|  |       expect(x.get(), 2); | ||||||
|  | 
 | ||||||
|  |       const Option<int> y = None(); | ||||||
|  |       expect(y.get(), null); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrap() returns value on Value', () { | ||||||
|  |       const Option<int> x = Value(2); | ||||||
|  |       expect(x.unwrap<int>(), 2); | ||||||
|  |       expect(() => x.unwrap<String>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrap() throws on None', () { | ||||||
|  |       const Option<int> x = None(); | ||||||
|  |       expect(() => x.unwrap<int>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrapAsync() returns value on Value', () async { | ||||||
|  |       const Option<int> x = Value(2); | ||||||
|  |       expect(await x.unwrapAsync<int>(), 2); | ||||||
|  |       expect(() async => x.unwrapAsync<String>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrapAsync() throws on Nonce', () async { | ||||||
|  |       const Option<int> x = None(); | ||||||
|  |       expect(() async => x.unwrapAsync<int>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('ifPresent() invokes command on Value', () { | ||||||
|  |       const Value<int>(2).ifPresent( | ||||||
|  |         print, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('orElse() returns value', () { | ||||||
|  |       expect( | ||||||
|  |         const Value<int>(10).orElse(12), | ||||||
|  |         10, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       expect( | ||||||
|  |         const None<int>().orElse(12), | ||||||
|  |         12, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('orElseLazy() returns value', () { | ||||||
|  |       expect( | ||||||
|  |         const Value<int>(10).orElseLazy(() => 12), | ||||||
|  |         10, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       expect( | ||||||
|  |         const None<int>().orElseLazy(() => 12), | ||||||
|  |         12, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'and() returns res if the option is `Value`, otherwise ' | ||||||
|  |       'returns `None`.', | ||||||
|  |       () { | ||||||
|  |         const Option<int> x1 = Value(2); | ||||||
|  |         const Option<int> y1 = None(); | ||||||
|  |         expect(x1.and(y1), y1); | ||||||
|  | 
 | ||||||
|  |         const Option<String> x2 = None(); | ||||||
|  |         const Option<String> y2 = Value('foo'); | ||||||
|  |         expect(x2.and(y2), x2); | ||||||
|  | 
 | ||||||
|  |         const Option<String> x3 = None(); | ||||||
|  |         const Option<String> y3 = None(); | ||||||
|  |         expect(x3.and(y3), x3); | ||||||
|  | 
 | ||||||
|  |         const Option<dynamic> x4 = Value(2); | ||||||
|  |         const Option<dynamic> y4 = Value('different Option type'); | ||||||
|  |         expect(x4.and(y4), y4); | ||||||
|  | 
 | ||||||
|  |         const Option<int> x5 = Value(2); | ||||||
|  |         const Option<int> y5 = Value(5); | ||||||
|  |         expect(x5.and(y5), y5); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'or() returns res if the option is `None`, otherwise ' | ||||||
|  |       'returns the `Value`.', | ||||||
|  |       () { | ||||||
|  |         const Option<int> x1 = Value(2); | ||||||
|  |         const Option<int> y1 = None(); | ||||||
|  |         expect(x1.or(y1), x1); | ||||||
|  | 
 | ||||||
|  |         const Option<String> x2 = None(); | ||||||
|  |         const Option<String> y2 = Value('foo'); | ||||||
|  |         expect(x2.or(y2), y2); | ||||||
|  | 
 | ||||||
|  |         const Option<void> x3 = None(); | ||||||
|  |         const Option<void> y3 = None(); | ||||||
|  |         expect(x3.or(y3), y3); | ||||||
|  | 
 | ||||||
|  |         const Option<dynamic> x4 = Value(2); | ||||||
|  |         const Option<dynamic> y4 = Value('different result type'); | ||||||
|  |         expect(x4.or(y4), x4); | ||||||
|  | 
 | ||||||
|  |         const Option<int> x5 = Value(2); | ||||||
|  |         const Option<int> y5 = Value(5); | ||||||
|  |         expect(x5.or(y5), x5); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'contains() returns true if the option is ' | ||||||
|  |       'containing the given value.', | ||||||
|  |       () { | ||||||
|  |         const Option<int> x1 = Value(2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  |         const Option<String> x2 = None(); | ||||||
|  |         expect(x2.contains(2), false); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'expect() return value if the option is a Value ' | ||||||
|  |       'else throw Exception', | ||||||
|  |       () { | ||||||
|  |         const Option<int> x1 = Value(2); | ||||||
|  |         expect(x1.expect('Testing expect'), 2); | ||||||
|  | 
 | ||||||
|  |         const Option<int> x2 = None(); | ||||||
|  |         expect( | ||||||
|  |           () => x2.expect('Testing expect'), | ||||||
|  |           throwsA( | ||||||
|  |             predicate<OptionException>( | ||||||
|  |               (e) => e.message == ('Testing expect'), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'map() transforms Value.', | ||||||
|  |       () { | ||||||
|  |         const Option<bool> x1 = Value(true); | ||||||
|  | 
 | ||||||
|  |         expect(x1.get(), true); | ||||||
|  |         expect(x1.map((value) => false).get(), false); | ||||||
|  | 
 | ||||||
|  |         const Option<bool> x2 = None(); | ||||||
|  | 
 | ||||||
|  |         expect(x2.map((value) => false).isNull, true); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'mapAsync() transforms Value.', | ||||||
|  |       () async { | ||||||
|  |         const Option<bool> x1 = Value(true); | ||||||
|  | 
 | ||||||
|  |         expect(x1.get(), true); | ||||||
|  |         expect( | ||||||
|  |           (await x1.mapAsync((value) => Future.value(false))).get(), | ||||||
|  |           false, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         const Option<bool> x2 = None(); | ||||||
|  | 
 | ||||||
|  |         expect( | ||||||
|  |           (await x2.mapAsync((value) => Future.value(false))).isNull, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'of(T value) returns Option with Value', | ||||||
|  |       () { | ||||||
|  |         final Option<int> x1 = Option.of(2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  |         expect( | ||||||
|  |           () => Option.of(null), | ||||||
|  |           throwsA( | ||||||
|  |             predicate<OptionException>( | ||||||
|  |               (e) => e.message == ("Value can' be null. Use `ofNullable()`"), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       "ofNullable(T value) returns Option with Value and don't throw on Null", | ||||||
|  |       () { | ||||||
|  |         final Option<int> x1 = Option.ofNullable(2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  | 
 | ||||||
|  |         final Option<int> x2 = Option.ofNullable(null); | ||||||
|  |         expect(x2.isNull, true); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'ofLazy() returns Option with Value', | ||||||
|  |       () { | ||||||
|  |         final Option<int> x1 = Option.ofLazy(() => 2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  |         expect( | ||||||
|  |           () => Option.ofLazy(() => null), | ||||||
|  |           throwsA( | ||||||
|  |             predicate<OptionException>( | ||||||
|  |               (e) => | ||||||
|  |                   e.message == ("Value can' be null. Use `ofNullableLazy()`"), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       "ofNullableLazy() returns Option with Value and don't throw on Null", | ||||||
|  |       () { | ||||||
|  |         final Option<int> x1 = Option.ofNullableLazy(() => 2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  | 
 | ||||||
|  |         final Option<int> x2 = Option.ofNullableLazy(() => null); | ||||||
|  |         expect(x2.isNull, true); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										425
									
								
								packages/wyatt_type_utils/test/result_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								packages/wyatt_type_utils/test/result_test.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,425 @@ | |||||||
|  | // Copyright (C) 2022 WYATT GROUP | ||||||
|  | // Please see the AUTHORS file for details. | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | import 'package:test/test.dart'; | ||||||
|  | import 'package:wyatt_type_utils/src/either_base.dart'; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   group('Result<T,E>', () { | ||||||
|  |     test('`isOk` returns true on Ok value', () { | ||||||
|  |       expect( | ||||||
|  |         const Ok<void, void>(null).isOk, | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         const Err<void, void>(null).isOk, | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('`isErr` returns true on Err value', () { | ||||||
|  |       expect( | ||||||
|  |         const Ok<void, void>(null).isErr, | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         const Err<void, void>(null).isErr, | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('`ok` returns value on Ok value', () { | ||||||
|  |       const Result<int, String> x = Ok(2); | ||||||
|  |       expect(x.ok, 2); | ||||||
|  |       expect(x.err, null); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('`err` returns error on Err value', () { | ||||||
|  |       const Result<int, String> x = Err('error'); | ||||||
|  |       expect(x.ok, null); | ||||||
|  |       expect(x.err, 'error'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrap() returns value on Ok value', () { | ||||||
|  |       const Result<int, String> x = Ok(2); | ||||||
|  |       expect(x.unwrap<int>(), 2); | ||||||
|  |       expect(() => x.unwrap<String>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrap() returns error on Err value', () { | ||||||
|  |       const Result<int, String> x = Err('error'); | ||||||
|  |       expect(x.unwrap<String>(), 'error'); | ||||||
|  |       expect(() => x.unwrap<int>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrapAsync() returns value on Ok value', () async { | ||||||
|  |       const Result<int, String> x = Ok(2); | ||||||
|  |       expect(await x.unwrapAsync<int>(), 2); | ||||||
|  |       expect(() async => x.unwrapAsync<String>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('unwrapAsync() returns error on Err value', () async { | ||||||
|  |       const Result<int, String> x = Err('error'); | ||||||
|  |       expect(await x.unwrapAsync<String>(), 'error'); | ||||||
|  |       expect(() async => x.unwrapAsync<int>(), throwsA(isException)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('fold() returns right value', () { | ||||||
|  |       expect( | ||||||
|  |         const Ok<String, String>('') | ||||||
|  |             .fold<bool>((left) => true, (right) => false), | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         const Err<String, String>('') | ||||||
|  |             .fold<bool>((left) => true, (right) => false), | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('foldAsync() returns right value', () async { | ||||||
|  |       expect( | ||||||
|  |         await const Ok<String, String>('').foldAsync<bool>( | ||||||
|  |           (left) => Future.value(true), | ||||||
|  |           (right) => Future.value(false), | ||||||
|  |         ), | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         await const Err<String, String>('').foldAsync<bool>( | ||||||
|  |           (left) => Future.value(true), | ||||||
|  |           (right) => Future.value(false), | ||||||
|  |         ), | ||||||
|  |         false, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('swap() swaps values', () { | ||||||
|  |       const Result<int, int> x = Ok(10); | ||||||
|  |       expect(x.isOk, true); | ||||||
|  |       expect(x.isErr, false); | ||||||
|  |       expect(x.ok, 10); | ||||||
|  |       final Result<int, int> y = x.swap(); | ||||||
|  |       expect(y.isOk, false); | ||||||
|  |       expect(y.isErr, true); | ||||||
|  |       expect(y.err, 10); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'and() returns res if the result is `Ok`, otherwise ' | ||||||
|  |       'returns the `Err` value of this.', | ||||||
|  |       () { | ||||||
|  |         const Result<int, String> x1 = Ok(2); | ||||||
|  |         const Result<int, String> y1 = Err('late error'); | ||||||
|  |         expect(x1.and(y1), y1); | ||||||
|  | 
 | ||||||
|  |         const Result<String, String> x2 = Err('early error'); | ||||||
|  |         const Result<String, String> y2 = Ok('foo'); | ||||||
|  |         expect(x2.and(y2), x2); | ||||||
|  | 
 | ||||||
|  |         const Result<void, String> x3 = Err('not a 2'); | ||||||
|  |         const Result<void, String> y3 = Err('late error'); | ||||||
|  |         expect(x3.and(y3), x3); | ||||||
|  | 
 | ||||||
|  |         const Result<dynamic, void> x4 = Ok(2); | ||||||
|  |         const Result<dynamic, void> y4 = Ok('different result type'); | ||||||
|  |         expect(x4.and(y4), y4); | ||||||
|  | 
 | ||||||
|  |         const Result<int, void> x5 = Ok(2); | ||||||
|  |         const Result<int, void> y5 = Ok(5); | ||||||
|  |         expect(x5.and(y5), y5); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'or() returns res if the result is `Err`, otherwise ' | ||||||
|  |       'returns the `Ok` value of this.', | ||||||
|  |       () { | ||||||
|  |         const Result<int, String> x1 = Ok(2); | ||||||
|  |         const Result<int, String> y1 = Err('late error'); | ||||||
|  |         expect(x1.or(y1), x1); | ||||||
|  | 
 | ||||||
|  |         const Result<String, String> x2 = Err('early error'); | ||||||
|  |         const Result<String, String> y2 = Ok('foo'); | ||||||
|  |         expect(x2.or(y2), y2); | ||||||
|  | 
 | ||||||
|  |         const Result<void, String> x3 = Err('not a 2'); | ||||||
|  |         const Result<void, String> y3 = Err('late error'); | ||||||
|  |         expect(x3.or(y3), y3); | ||||||
|  | 
 | ||||||
|  |         const Result<dynamic, void> x4 = Ok(2); | ||||||
|  |         const Result<dynamic, void> y4 = Ok('different result type'); | ||||||
|  |         expect(x4.or(y4), x4); | ||||||
|  | 
 | ||||||
|  |         const Result<int, void> x5 = Ok(2); | ||||||
|  |         const Result<int, void> y5 = Ok(5); | ||||||
|  |         expect(x5.or(y5), x5); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'contains() returns true if the result is an Ok value ' | ||||||
|  |       'containing the given value.', | ||||||
|  |       () { | ||||||
|  |         const Result<int, void> x1 = Ok(2); | ||||||
|  |         expect(x1.contains(2), true); | ||||||
|  |         expect(x1.contains(3), false); | ||||||
|  |         const Result<void, String> x2 = Err('Some error message'); | ||||||
|  |         expect(x2.contains(2), false); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'expect() return value if the result is an Ok value ' | ||||||
|  |       'else throw Exception', | ||||||
|  |       () { | ||||||
|  |         const Result<int, String> x1 = Ok(2); | ||||||
|  |         expect(x1.expect('Testing expect'), 2); | ||||||
|  | 
 | ||||||
|  |         const Result<int, String> x2 = Err('emergency failure'); | ||||||
|  |         expect( | ||||||
|  |           () => x2.expect('Testing expect'), | ||||||
|  |           throwsA( | ||||||
|  |             predicate<ResultException>( | ||||||
|  |               (e) => e.message == ('Testing expect: emergency failure'), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'expectErr() return value if the result is an Err value ' | ||||||
|  |       'else throw Exception', | ||||||
|  |       () { | ||||||
|  |         const Result<String, int> x1 = Err(2); | ||||||
|  |         expect(x1.expectErr('Testing expect err'), 2); | ||||||
|  | 
 | ||||||
|  |         const Result<String, int> x2 = Ok('success value'); | ||||||
|  |         expect( | ||||||
|  |           () => x2.expectErr('Testing expect err'), | ||||||
|  |           throwsA( | ||||||
|  |             predicate<ResultException>( | ||||||
|  |               (e) => e.message == ('Testing expect err: success value'), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'map() transforms Ok value.', | ||||||
|  |       () { | ||||||
|  |         const Result<bool, void> x1 = Ok(true); | ||||||
|  | 
 | ||||||
|  |         expect(x1.ok, true); | ||||||
|  |         expect(x1.map((value) => false).ok, false); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, String> x2 = Err('oops'); | ||||||
|  | 
 | ||||||
|  |         expect(x2.map((value) => false).isErr, true); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'mapErr() transforms Err value.', | ||||||
|  |       () { | ||||||
|  |         const Result<bool, String> x1 = Ok(true); | ||||||
|  | 
 | ||||||
|  |         expect(x1.mapErr((value) => false).isOk, true); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, String> x2 = Err('oops'); | ||||||
|  | 
 | ||||||
|  |         expect(x2.err, 'oops'); | ||||||
|  |         expect(x2.mapErr((error) => 'failure').err, 'failure'); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'mapAsync() transforms Ok value.', | ||||||
|  |       () async { | ||||||
|  |         const Result<bool, void> x1 = Ok(true); | ||||||
|  | 
 | ||||||
|  |         expect(x1.ok, true); | ||||||
|  |         expect((await x1.mapAsync((value) => Future.value(false))).ok, false); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, String> x2 = Err('oops'); | ||||||
|  | 
 | ||||||
|  |         expect((await x2.mapAsync((value) => Future.value(false))).isErr, true); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'mapErrAsync() transforms Err value.', | ||||||
|  |       () async { | ||||||
|  |         const Result<bool, String> x1 = Ok(true); | ||||||
|  | 
 | ||||||
|  |         expect( | ||||||
|  |           (await x1.mapErrAsync((value) => Future.value(false))).isOk, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, String> x2 = Err('oops'); | ||||||
|  | 
 | ||||||
|  |         expect(x2.err, 'oops'); | ||||||
|  |         expect( | ||||||
|  |           (await x2.mapErrAsync((error) => Future.value('failure'))).err, | ||||||
|  |           'failure', | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'either() transforms values.', | ||||||
|  |       () { | ||||||
|  |         const Result<bool, bool> x1 = Ok(true); | ||||||
|  |         final Result<int, String> y1 = | ||||||
|  |             x1.either<int, String>((value) => 1, (error) => 'error'); | ||||||
|  | 
 | ||||||
|  |         expect(y1.isOk, true); | ||||||
|  |         expect(y1.ok, 1); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, bool> x2 = Err(true); | ||||||
|  |         final Result<int, String> y2 = | ||||||
|  |             x2.either<int, String>((value) => 1, (error) => 'error'); | ||||||
|  | 
 | ||||||
|  |         expect(y2.isErr, true); | ||||||
|  |         expect(y2.err, 'error'); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'eitherAsync() transforms values.', | ||||||
|  |       () async { | ||||||
|  |         const Result<bool, bool> x1 = Ok(true); | ||||||
|  |         final Result<int, String> y1 = await x1.eitherAsync<int, String>( | ||||||
|  |           (value) => Future.value(1), | ||||||
|  |           (error) => Future.value('error'), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(y1.isOk, true); | ||||||
|  |         expect(y1.ok, 1); | ||||||
|  | 
 | ||||||
|  |         const Result<bool, bool> x2 = Err(true); | ||||||
|  |         final Result<int, String> y2 = await x2.eitherAsync<int, String>( | ||||||
|  |           (value) => Future.value(1), | ||||||
|  |           (error) => Future.value('error'), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(y2.isErr, true); | ||||||
|  |         expect(y2.err, 'error'); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'tryCatch() try and catch error as Err.', | ||||||
|  |       () { | ||||||
|  |         expect( | ||||||
|  |           Result.tryCatch<int, String, Exception>( | ||||||
|  |             () => throw Exception('not success'), | ||||||
|  |             (err) => 'error', | ||||||
|  |           ).isErr, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           Result.tryCatch<int, String, Exception>( | ||||||
|  |             () => 2, | ||||||
|  |             (err) => 'error', | ||||||
|  |           ).isOk, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'tryCatchAsync() try and catch error as Err.', | ||||||
|  |       () async { | ||||||
|  |         expect( | ||||||
|  |           (await Result.tryCatchAsync<int, String, Exception>( | ||||||
|  |             () => throw Exception('not success'), | ||||||
|  |             (err) => 'error', | ||||||
|  |           )).isErr, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           (await Result.tryCatchAsync<int, String, Exception>( | ||||||
|  |             () => Future.value(2), | ||||||
|  |             (err) => 'error', | ||||||
|  |           )).isOk, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'conditional() returns Result on true test', | ||||||
|  |       () { | ||||||
|  |         final Result<int, String> x1 = Result.conditional<int, String>( | ||||||
|  |           false, | ||||||
|  |           2, | ||||||
|  |           'error', | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           x1.isErr, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(x1.err, 'error'); | ||||||
|  | 
 | ||||||
|  |         final Result<int, String> x2 = Result.conditional<int, String>( | ||||||
|  |           true, | ||||||
|  |           2, | ||||||
|  |           'error', | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           x2.isOk, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(x2.ok, 2); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     test( | ||||||
|  |       'conditionalLazy() returns Result on true test', | ||||||
|  |       () { | ||||||
|  |         final Result<int, String> x1 = Result.conditionalLazy<int, String>( | ||||||
|  |           false, | ||||||
|  |           () => 2, | ||||||
|  |           () => 'error', | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           x1.isErr, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(x1.err, 'error'); | ||||||
|  | 
 | ||||||
|  |         final Result<int, String> x2 = Result.conditionalLazy<int, String>( | ||||||
|  |           true, | ||||||
|  |           () => 2, | ||||||
|  |           () => 'error', | ||||||
|  |         ); | ||||||
|  |         expect( | ||||||
|  |           x2.isOk, | ||||||
|  |           true, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         expect(x2.ok, 2); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user