import 'package:dio/dio.dart'; import 'package:hiddify/core/prefs/prefs.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: null), }; 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), }; }