初始化提交

This commit is contained in:
2025-09-23 16:23:15 +08:00
commit 877b18a70f
590 changed files with 53617 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
import 'dart:io';
typedef Directories = ({
Directory baseDir,
Directory workingDir,
Directory tempDir
});
+73
View File
@@ -0,0 +1,73 @@
// import 'package:dio/dio.dart';
// import 'package:kaer_with_panels/app/localization/translations.g.dart';
// typedef PresentableError = ({String type, String? message});
// mixin Failure {
// ({String type, String? message}) present(TranslationsEn t);
// }
// /// failures that are not expected to happen but depending on [error] type might not be relevant (eg network errors)
// mixin UnexpectedFailure {
// Object? get error;
// StackTrace? get stackTrace;
// }
// /// failures that are expected to happen and should be handled by the app
// /// and should be logged, eg missing permissions
// mixin ExpectedMeasuredFailure {}
// /// failures ignored by analytics service etc.
// mixin ExpectedFailure {}
// extension ErrorPresenter on TranslationsEn {
// PresentableError errorToPair(Object error) => switch (error) {
// UnexpectedFailure(error: final nestedErr?) => errorToPair(nestedErr),
// Failure() => error.present(this),
// DioException() => error.present(this),
// _ => (type: failure.unexpected, message: error.toString()),
// };
// PresentableError presentError(
// Object error, {
// String? action,
// }) {
// final pair = errorToPair(error);
// if (action == null) return pair;
// return (
// type: action,
// message: pair.type + (pair.message == null ? "" : "\n${pair.message!}"),
// );
// }
// String presentShortError(
// Object error, {
// String? action,
// }) {
// final pair = errorToPair(error);
// if (action == null) return pair.type;
// return "$action: ${pair.type}";
// }
// }
// extension DioExceptionPresenter on DioException {
// PresentableError present(TranslationsEn t) => switch (type) {
// DioExceptionType.connectionTimeout ||
// DioExceptionType.sendTimeout ||
// DioExceptionType.receiveTimeout =>
// (type: t.failure.connection.timeout, message: null),
// DioExceptionType.badCertificate => (
// type: t.failure.connection.badCertificate,
// message: message,
// ),
// DioExceptionType.badResponse => (
// type: t.failure.connection.badResponse,
// message: message,
// ),
// DioExceptionType.connectionError => (
// type: t.failure.connection.connectionError,
// message: message,
// ),
// _ => (type: t.failure.connection.unexpected, message: message),
// };
// }
+56
View File
@@ -0,0 +1,56 @@
import 'package:dart_mappable/dart_mappable.dart';
import 'package:dartx/dartx.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
// import 'package:kaer_with_panels/core/localization/translations.dart';
part 'optional_range.mapper.dart';
@MappableClass()
class OptionalRange with OptionalRangeMappable {
const OptionalRange({this.min, this.max});
final int? min;
final int? max;
String format() => [min, max].whereNotNull().join("-");
// String present(TranslationsEn t) =>
// format().isEmpty ? t.general.notSet : format();
factory OptionalRange.parse(
String input, {
bool allowEmpty = false,
}) =>
switch (input.split("-")) {
[final String val] when val.isEmpty && allowEmpty =>
const OptionalRange(),
[final String min] => OptionalRange(min: int.parse(min)),
[final String min, final String max] => OptionalRange(
min: int.parse(min),
max: int.parse(max),
),
_ => throw Exception("Invalid range: $input"),
};
static OptionalRange? tryParse(
String input, {
bool allowEmpty = false,
}) {
try {
return OptionalRange.parse(input, allowEmpty: allowEmpty);
} catch (_) {
return null;
}
}
}
class OptionalRangeJsonConverter
implements JsonConverter<OptionalRange, String> {
const OptionalRangeJsonConverter();
@override
OptionalRange fromJson(String json) =>
OptionalRange.parse(json, allowEmpty: true);
@override
String toJson(OptionalRange object) => object.format();
}
+48
View File
@@ -0,0 +1,48 @@
import 'package:fpdart/fpdart.dart';
import 'package:kaer_with_panels/utils/utils.dart';
import 'package:rxdart/rxdart.dart';
mixin ExceptionHandler implements LoggerMixin {
TaskEither<F, R> exceptionHandler<F, R>(
Future<Either<F, R>> Function() run,
F Function(Object error, StackTrace stackTrace) onError,
) {
return TaskEither(
() async {
try {
return await run();
} catch (error, stackTrace) {
return Left(onError(error, stackTrace));
}
},
);
}
}
extension StreamExceptionHandler<R extends Object?> on Stream<R> {
Stream<Either<F, R>> handleExceptions<F>(
F Function(Object error, StackTrace stackTrace) onError,
) {
return map(right<F, R>).onErrorReturnWith(
(error, stackTrace) {
return Left(onError(error, stackTrace));
},
);
}
}
extension TaskEitherExceptionHandler<F, R> on TaskEither<F, R> {
TaskEither<F, R> handleExceptions(
F Function(Object error, StackTrace stackTrace) onError,
) {
return TaskEither(
() async {
try {
return await run();
} catch (error, stackTrace) {
return Left(onError(error, stackTrace));
}
},
);
}
}
+11
View File
@@ -0,0 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
class IntervalInSecondsConverter implements JsonConverter<Duration, int> {
const IntervalInSecondsConverter();
@override
Duration fromJson(int json) => Duration(seconds: json);
@override
int toJson(Duration object) => object.inSeconds;
}