mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 06:33:43 +08:00
新增 Rust 实现的多线程下载器,优化下载可靠性
This commit is contained in:
parent
3409a8597f
commit
5bc7024fe7
@ -1,4 +1,5 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:starcitizen_doctor/common/conf.dart';
|
import 'package:starcitizen_doctor/common/conf.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||||
|
|
||||||
@ -6,6 +7,8 @@ class AnalyticsApi {
|
|||||||
static final Dio _dio = Dio();
|
static final Dio _dio = Dio();
|
||||||
|
|
||||||
static touch(String key) async {
|
static touch(String key) async {
|
||||||
|
// debug 不统计
|
||||||
|
if (kDebugMode) return;
|
||||||
dPrint("AnalyticsApi.touch === $key start");
|
dPrint("AnalyticsApi.touch === $key start");
|
||||||
try {
|
try {
|
||||||
await _dio.post("${AppConf.xkeycApiUrl}/analytics/$key");
|
await _dio.post("${AppConf.xkeycApiUrl}/analytics/$key");
|
||||||
|
@ -6,6 +6,7 @@ import 'package:hive/hive.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:starcitizen_doctor/api/analytics.dart';
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||||
import 'package:starcitizen_doctor/api/api.dart';
|
import 'package:starcitizen_doctor/api/api.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/rust/ffi.dart';
|
||||||
import 'package:starcitizen_doctor/data/app_version_data.dart';
|
import 'package:starcitizen_doctor/data/app_version_data.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
@ -65,6 +66,13 @@ class AppConf {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// check Rust bridge
|
||||||
|
if (await rustFii.ping() != "PONG") {
|
||||||
|
dPrint("Rust bridge Error");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
dPrint("---- rust bridge inited -----");
|
||||||
|
|
||||||
/// init windows
|
/// init windows
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
windowManager.waitUntilReadyToShow().then((_) async {
|
windowManager.waitUntilReadyToShow().then((_) async {
|
||||||
|
@ -7,28 +7,61 @@ import 'dart:async';
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
|
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
||||||
|
|
||||||
|
part 'bridge_definitions.freezed.dart';
|
||||||
|
|
||||||
abstract class Rust {
|
abstract class Rust {
|
||||||
Future<Platform> platform({dynamic hint});
|
Future<String> ping({dynamic hint});
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kPlatformConstMeta;
|
FlutterRustBridgeTaskConstMeta get kPingConstMeta;
|
||||||
|
|
||||||
Future<int> add({required int left, required int right, dynamic hint});
|
Stream<DownloadCallbackData> startDownload(
|
||||||
|
{required String url,
|
||||||
|
required String savePath,
|
||||||
|
required String fileName,
|
||||||
|
required int connectionCount,
|
||||||
|
dynamic hint});
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kAddConstMeta;
|
FlutterRustBridgeTaskConstMeta get kStartDownloadConstMeta;
|
||||||
|
|
||||||
Future<bool> rustReleaseMode({dynamic hint});
|
Future<void> cancelDownload({required String id, dynamic hint});
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta;
|
FlutterRustBridgeTaskConstMeta get kCancelDownloadConstMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Platform {
|
class DownloadCallbackData {
|
||||||
Unknown,
|
final String id;
|
||||||
Android,
|
final int total;
|
||||||
Ios,
|
final int progress;
|
||||||
Windows,
|
final int speed;
|
||||||
Unix,
|
final MyDownloaderStatus status;
|
||||||
MacIntel,
|
|
||||||
MacApple,
|
const DownloadCallbackData({
|
||||||
Wasm,
|
required this.id,
|
||||||
|
required this.total,
|
||||||
|
required this.progress,
|
||||||
|
required this.speed,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class MyDownloaderStatus with _$MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus.noStart() = MyDownloaderStatus_NoStart;
|
||||||
|
const factory MyDownloaderStatus.running() = MyDownloaderStatus_Running;
|
||||||
|
const factory MyDownloaderStatus.pending(
|
||||||
|
MyNetworkItemPendingType field0,
|
||||||
|
) = MyDownloaderStatus_Pending;
|
||||||
|
const factory MyDownloaderStatus.error(
|
||||||
|
String field0,
|
||||||
|
) = MyDownloaderStatus_Error;
|
||||||
|
const factory MyDownloaderStatus.finished() = MyDownloaderStatus_Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyNetworkItemPendingType {
|
||||||
|
QueueUp,
|
||||||
|
Starting,
|
||||||
|
Stopping,
|
||||||
|
Initializing,
|
||||||
}
|
}
|
||||||
|
778
lib/common/rust/bridge_definitions.freezed.dart
Normal file
778
lib/common/rust/bridge_definitions.freezed.dart
Normal file
@ -0,0 +1,778 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'bridge_definitions.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$MyDownloaderStatus {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $MyDownloaderStatusCopyWith<$Res> {
|
||||||
|
factory $MyDownloaderStatusCopyWith(
|
||||||
|
MyDownloaderStatus value, $Res Function(MyDownloaderStatus) then) =
|
||||||
|
_$MyDownloaderStatusCopyWithImpl<$Res, MyDownloaderStatus>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$MyDownloaderStatusCopyWithImpl<$Res, $Val extends MyDownloaderStatus>
|
||||||
|
implements $MyDownloaderStatusCopyWith<$Res> {
|
||||||
|
_$MyDownloaderStatusCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$MyDownloaderStatus_NoStartImplCopyWith<$Res> {
|
||||||
|
factory _$$MyDownloaderStatus_NoStartImplCopyWith(
|
||||||
|
_$MyDownloaderStatus_NoStartImpl value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_NoStartImpl) then) =
|
||||||
|
__$$MyDownloaderStatus_NoStartImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$MyDownloaderStatus_NoStartImplCopyWithImpl<$Res>
|
||||||
|
extends _$MyDownloaderStatusCopyWithImpl<$Res,
|
||||||
|
_$MyDownloaderStatus_NoStartImpl>
|
||||||
|
implements _$$MyDownloaderStatus_NoStartImplCopyWith<$Res> {
|
||||||
|
__$$MyDownloaderStatus_NoStartImplCopyWithImpl(
|
||||||
|
_$MyDownloaderStatus_NoStartImpl _value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_NoStartImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$MyDownloaderStatus_NoStartImpl implements MyDownloaderStatus_NoStart {
|
||||||
|
const _$MyDownloaderStatus_NoStartImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MyDownloaderStatus.noStart()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$MyDownloaderStatus_NoStartImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) {
|
||||||
|
return noStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) {
|
||||||
|
return noStart?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (noStart != null) {
|
||||||
|
return noStart();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) {
|
||||||
|
return noStart(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) {
|
||||||
|
return noStart?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (noStart != null) {
|
||||||
|
return noStart(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MyDownloaderStatus_NoStart implements MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus_NoStart() = _$MyDownloaderStatus_NoStartImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$MyDownloaderStatus_RunningImplCopyWith<$Res> {
|
||||||
|
factory _$$MyDownloaderStatus_RunningImplCopyWith(
|
||||||
|
_$MyDownloaderStatus_RunningImpl value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_RunningImpl) then) =
|
||||||
|
__$$MyDownloaderStatus_RunningImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$MyDownloaderStatus_RunningImplCopyWithImpl<$Res>
|
||||||
|
extends _$MyDownloaderStatusCopyWithImpl<$Res,
|
||||||
|
_$MyDownloaderStatus_RunningImpl>
|
||||||
|
implements _$$MyDownloaderStatus_RunningImplCopyWith<$Res> {
|
||||||
|
__$$MyDownloaderStatus_RunningImplCopyWithImpl(
|
||||||
|
_$MyDownloaderStatus_RunningImpl _value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_RunningImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$MyDownloaderStatus_RunningImpl implements MyDownloaderStatus_Running {
|
||||||
|
const _$MyDownloaderStatus_RunningImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MyDownloaderStatus.running()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$MyDownloaderStatus_RunningImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) {
|
||||||
|
return running();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) {
|
||||||
|
return running?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (running != null) {
|
||||||
|
return running();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) {
|
||||||
|
return running(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) {
|
||||||
|
return running?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (running != null) {
|
||||||
|
return running(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MyDownloaderStatus_Running implements MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus_Running() = _$MyDownloaderStatus_RunningImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$MyDownloaderStatus_PendingImplCopyWith<$Res> {
|
||||||
|
factory _$$MyDownloaderStatus_PendingImplCopyWith(
|
||||||
|
_$MyDownloaderStatus_PendingImpl value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_PendingImpl) then) =
|
||||||
|
__$$MyDownloaderStatus_PendingImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({MyNetworkItemPendingType field0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$MyDownloaderStatus_PendingImplCopyWithImpl<$Res>
|
||||||
|
extends _$MyDownloaderStatusCopyWithImpl<$Res,
|
||||||
|
_$MyDownloaderStatus_PendingImpl>
|
||||||
|
implements _$$MyDownloaderStatus_PendingImplCopyWith<$Res> {
|
||||||
|
__$$MyDownloaderStatus_PendingImplCopyWithImpl(
|
||||||
|
_$MyDownloaderStatus_PendingImpl _value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_PendingImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? field0 = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$MyDownloaderStatus_PendingImpl(
|
||||||
|
null == field0
|
||||||
|
? _value.field0
|
||||||
|
: field0 // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MyNetworkItemPendingType,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$MyDownloaderStatus_PendingImpl implements MyDownloaderStatus_Pending {
|
||||||
|
const _$MyDownloaderStatus_PendingImpl(this.field0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final MyNetworkItemPendingType field0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MyDownloaderStatus.pending(field0: $field0)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$MyDownloaderStatus_PendingImpl &&
|
||||||
|
(identical(other.field0, field0) || other.field0 == field0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, field0);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$MyDownloaderStatus_PendingImplCopyWith<_$MyDownloaderStatus_PendingImpl>
|
||||||
|
get copyWith => __$$MyDownloaderStatus_PendingImplCopyWithImpl<
|
||||||
|
_$MyDownloaderStatus_PendingImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) {
|
||||||
|
return pending(field0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) {
|
||||||
|
return pending?.call(field0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (pending != null) {
|
||||||
|
return pending(field0);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) {
|
||||||
|
return pending(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) {
|
||||||
|
return pending?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (pending != null) {
|
||||||
|
return pending(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MyDownloaderStatus_Pending implements MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus_Pending(
|
||||||
|
final MyNetworkItemPendingType field0) = _$MyDownloaderStatus_PendingImpl;
|
||||||
|
|
||||||
|
MyNetworkItemPendingType get field0;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$MyDownloaderStatus_PendingImplCopyWith<_$MyDownloaderStatus_PendingImpl>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$MyDownloaderStatus_ErrorImplCopyWith<$Res> {
|
||||||
|
factory _$$MyDownloaderStatus_ErrorImplCopyWith(
|
||||||
|
_$MyDownloaderStatus_ErrorImpl value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_ErrorImpl) then) =
|
||||||
|
__$$MyDownloaderStatus_ErrorImplCopyWithImpl<$Res>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String field0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$MyDownloaderStatus_ErrorImplCopyWithImpl<$Res>
|
||||||
|
extends _$MyDownloaderStatusCopyWithImpl<$Res,
|
||||||
|
_$MyDownloaderStatus_ErrorImpl>
|
||||||
|
implements _$$MyDownloaderStatus_ErrorImplCopyWith<$Res> {
|
||||||
|
__$$MyDownloaderStatus_ErrorImplCopyWithImpl(
|
||||||
|
_$MyDownloaderStatus_ErrorImpl _value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_ErrorImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? field0 = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$MyDownloaderStatus_ErrorImpl(
|
||||||
|
null == field0
|
||||||
|
? _value.field0
|
||||||
|
: field0 // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$MyDownloaderStatus_ErrorImpl implements MyDownloaderStatus_Error {
|
||||||
|
const _$MyDownloaderStatus_ErrorImpl(this.field0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String field0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MyDownloaderStatus.error(field0: $field0)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$MyDownloaderStatus_ErrorImpl &&
|
||||||
|
(identical(other.field0, field0) || other.field0 == field0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, field0);
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$MyDownloaderStatus_ErrorImplCopyWith<_$MyDownloaderStatus_ErrorImpl>
|
||||||
|
get copyWith => __$$MyDownloaderStatus_ErrorImplCopyWithImpl<
|
||||||
|
_$MyDownloaderStatus_ErrorImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) {
|
||||||
|
return error(field0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) {
|
||||||
|
return error?.call(field0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (error != null) {
|
||||||
|
return error(field0);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) {
|
||||||
|
return error(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) {
|
||||||
|
return error?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (error != null) {
|
||||||
|
return error(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MyDownloaderStatus_Error implements MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus_Error(final String field0) =
|
||||||
|
_$MyDownloaderStatus_ErrorImpl;
|
||||||
|
|
||||||
|
String get field0;
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$MyDownloaderStatus_ErrorImplCopyWith<_$MyDownloaderStatus_ErrorImpl>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$MyDownloaderStatus_FinishedImplCopyWith<$Res> {
|
||||||
|
factory _$$MyDownloaderStatus_FinishedImplCopyWith(
|
||||||
|
_$MyDownloaderStatus_FinishedImpl value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_FinishedImpl) then) =
|
||||||
|
__$$MyDownloaderStatus_FinishedImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$MyDownloaderStatus_FinishedImplCopyWithImpl<$Res>
|
||||||
|
extends _$MyDownloaderStatusCopyWithImpl<$Res,
|
||||||
|
_$MyDownloaderStatus_FinishedImpl>
|
||||||
|
implements _$$MyDownloaderStatus_FinishedImplCopyWith<$Res> {
|
||||||
|
__$$MyDownloaderStatus_FinishedImplCopyWithImpl(
|
||||||
|
_$MyDownloaderStatus_FinishedImpl _value,
|
||||||
|
$Res Function(_$MyDownloaderStatus_FinishedImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$MyDownloaderStatus_FinishedImpl implements MyDownloaderStatus_Finished {
|
||||||
|
const _$MyDownloaderStatus_FinishedImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'MyDownloaderStatus.finished()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$MyDownloaderStatus_FinishedImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() noStart,
|
||||||
|
required TResult Function() running,
|
||||||
|
required TResult Function(MyNetworkItemPendingType field0) pending,
|
||||||
|
required TResult Function(String field0) error,
|
||||||
|
required TResult Function() finished,
|
||||||
|
}) {
|
||||||
|
return finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? noStart,
|
||||||
|
TResult? Function()? running,
|
||||||
|
TResult? Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult? Function(String field0)? error,
|
||||||
|
TResult? Function()? finished,
|
||||||
|
}) {
|
||||||
|
return finished?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? noStart,
|
||||||
|
TResult Function()? running,
|
||||||
|
TResult Function(MyNetworkItemPendingType field0)? pending,
|
||||||
|
TResult Function(String field0)? error,
|
||||||
|
TResult Function()? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (finished != null) {
|
||||||
|
return finished();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(MyDownloaderStatus_NoStart value) noStart,
|
||||||
|
required TResult Function(MyDownloaderStatus_Running value) running,
|
||||||
|
required TResult Function(MyDownloaderStatus_Pending value) pending,
|
||||||
|
required TResult Function(MyDownloaderStatus_Error value) error,
|
||||||
|
required TResult Function(MyDownloaderStatus_Finished value) finished,
|
||||||
|
}) {
|
||||||
|
return finished(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult? Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult? Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult? Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult? Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
}) {
|
||||||
|
return finished?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(MyDownloaderStatus_NoStart value)? noStart,
|
||||||
|
TResult Function(MyDownloaderStatus_Running value)? running,
|
||||||
|
TResult Function(MyDownloaderStatus_Pending value)? pending,
|
||||||
|
TResult Function(MyDownloaderStatus_Error value)? error,
|
||||||
|
TResult Function(MyDownloaderStatus_Finished value)? finished,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (finished != null) {
|
||||||
|
return finished(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class MyDownloaderStatus_Finished implements MyDownloaderStatus {
|
||||||
|
const factory MyDownloaderStatus_Finished() =
|
||||||
|
_$MyDownloaderStatus_FinishedImpl;
|
||||||
|
}
|
@ -25,57 +25,66 @@ class RustImpl implements Rust {
|
|||||||
factory RustImpl.wasm(FutureOr<WasmModule> module) =>
|
factory RustImpl.wasm(FutureOr<WasmModule> module) =>
|
||||||
RustImpl(module as ExternalLibrary);
|
RustImpl(module as ExternalLibrary);
|
||||||
RustImpl.raw(this._platform);
|
RustImpl.raw(this._platform);
|
||||||
Future<Platform> platform({dynamic hint}) {
|
Future<String> ping({dynamic hint}) {
|
||||||
return _platform.executeNormal(FlutterRustBridgeTask(
|
return _platform.executeNormal(FlutterRustBridgeTask(
|
||||||
callFfi: (port_) => _platform.inner.wire_platform(port_),
|
callFfi: (port_) => _platform.inner.wire_ping(port_),
|
||||||
parseSuccessData: _wire2api_platform,
|
parseSuccessData: _wire2api_String,
|
||||||
parseErrorData: null,
|
parseErrorData: null,
|
||||||
constMeta: kPlatformConstMeta,
|
constMeta: kPingConstMeta,
|
||||||
argValues: [],
|
argValues: [],
|
||||||
hint: hint,
|
hint: hint,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kPlatformConstMeta =>
|
FlutterRustBridgeTaskConstMeta get kPingConstMeta =>
|
||||||
const FlutterRustBridgeTaskConstMeta(
|
const FlutterRustBridgeTaskConstMeta(
|
||||||
debugName: "platform",
|
debugName: "ping",
|
||||||
argNames: [],
|
argNames: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<int> add({required int left, required int right, dynamic hint}) {
|
Stream<DownloadCallbackData> startDownload(
|
||||||
var arg0 = api2wire_usize(left);
|
{required String url,
|
||||||
var arg1 = api2wire_usize(right);
|
required String savePath,
|
||||||
return _platform.executeNormal(FlutterRustBridgeTask(
|
required String fileName,
|
||||||
callFfi: (port_) => _platform.inner.wire_add(port_, arg0, arg1),
|
required int connectionCount,
|
||||||
parseSuccessData: _wire2api_usize,
|
dynamic hint}) {
|
||||||
|
var arg0 = _platform.api2wire_String(url);
|
||||||
|
var arg1 = _platform.api2wire_String(savePath);
|
||||||
|
var arg2 = _platform.api2wire_String(fileName);
|
||||||
|
var arg3 = api2wire_u8(connectionCount);
|
||||||
|
return _platform.executeStream(FlutterRustBridgeTask(
|
||||||
|
callFfi: (port_) =>
|
||||||
|
_platform.inner.wire_start_download(port_, arg0, arg1, arg2, arg3),
|
||||||
|
parseSuccessData: _wire2api_download_callback_data,
|
||||||
parseErrorData: null,
|
parseErrorData: null,
|
||||||
constMeta: kAddConstMeta,
|
constMeta: kStartDownloadConstMeta,
|
||||||
argValues: [left, right],
|
argValues: [url, savePath, fileName, connectionCount],
|
||||||
hint: hint,
|
hint: hint,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kAddConstMeta =>
|
FlutterRustBridgeTaskConstMeta get kStartDownloadConstMeta =>
|
||||||
const FlutterRustBridgeTaskConstMeta(
|
const FlutterRustBridgeTaskConstMeta(
|
||||||
debugName: "add",
|
debugName: "start_download",
|
||||||
argNames: ["left", "right"],
|
argNames: ["url", "savePath", "fileName", "connectionCount"],
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<bool> rustReleaseMode({dynamic hint}) {
|
Future<void> cancelDownload({required String id, dynamic hint}) {
|
||||||
|
var arg0 = _platform.api2wire_String(id);
|
||||||
return _platform.executeNormal(FlutterRustBridgeTask(
|
return _platform.executeNormal(FlutterRustBridgeTask(
|
||||||
callFfi: (port_) => _platform.inner.wire_rust_release_mode(port_),
|
callFfi: (port_) => _platform.inner.wire_cancel_download(port_, arg0),
|
||||||
parseSuccessData: _wire2api_bool,
|
parseSuccessData: _wire2api_unit,
|
||||||
parseErrorData: null,
|
parseErrorData: null,
|
||||||
constMeta: kRustReleaseModeConstMeta,
|
constMeta: kCancelDownloadConstMeta,
|
||||||
argValues: [],
|
argValues: [id],
|
||||||
hint: hint,
|
hint: hint,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta =>
|
FlutterRustBridgeTaskConstMeta get kCancelDownloadConstMeta =>
|
||||||
const FlutterRustBridgeTaskConstMeta(
|
const FlutterRustBridgeTaskConstMeta(
|
||||||
debugName: "rust_release_mode",
|
debugName: "cancel_download",
|
||||||
argNames: [],
|
argNames: ["id"],
|
||||||
);
|
);
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -83,29 +92,76 @@ class RustImpl implements Rust {
|
|||||||
}
|
}
|
||||||
// Section: wire2api
|
// Section: wire2api
|
||||||
|
|
||||||
bool _wire2api_bool(dynamic raw) {
|
String _wire2api_String(dynamic raw) {
|
||||||
return raw as bool;
|
return raw as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadCallbackData _wire2api_download_callback_data(dynamic raw) {
|
||||||
|
final arr = raw as List<dynamic>;
|
||||||
|
if (arr.length != 5)
|
||||||
|
throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||||
|
return DownloadCallbackData(
|
||||||
|
id: _wire2api_String(arr[0]),
|
||||||
|
total: _wire2api_u64(arr[1]),
|
||||||
|
progress: _wire2api_u64(arr[2]),
|
||||||
|
speed: _wire2api_u64(arr[3]),
|
||||||
|
status: _wire2api_my_downloader_status(arr[4]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _wire2api_i32(dynamic raw) {
|
int _wire2api_i32(dynamic raw) {
|
||||||
return raw as int;
|
return raw as int;
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform _wire2api_platform(dynamic raw) {
|
MyDownloaderStatus _wire2api_my_downloader_status(dynamic raw) {
|
||||||
return Platform.values[raw as int];
|
switch (raw[0]) {
|
||||||
|
case 0:
|
||||||
|
return MyDownloaderStatus_NoStart();
|
||||||
|
case 1:
|
||||||
|
return MyDownloaderStatus_Running();
|
||||||
|
case 2:
|
||||||
|
return MyDownloaderStatus_Pending(
|
||||||
|
_wire2api_my_network_item_pending_type(raw[1]),
|
||||||
|
);
|
||||||
|
case 3:
|
||||||
|
return MyDownloaderStatus_Error(
|
||||||
|
_wire2api_String(raw[1]),
|
||||||
|
);
|
||||||
|
case 4:
|
||||||
|
return MyDownloaderStatus_Finished();
|
||||||
|
default:
|
||||||
|
throw Exception("unreachable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int _wire2api_usize(dynamic raw) {
|
MyNetworkItemPendingType _wire2api_my_network_item_pending_type(dynamic raw) {
|
||||||
|
return MyNetworkItemPendingType.values[raw as int];
|
||||||
|
}
|
||||||
|
|
||||||
|
int _wire2api_u64(dynamic raw) {
|
||||||
return castInt(raw);
|
return castInt(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _wire2api_u8(dynamic raw) {
|
||||||
|
return raw as int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List _wire2api_uint_8_list(dynamic raw) {
|
||||||
|
return raw as Uint8List;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _wire2api_unit(dynamic raw) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section: api2wire
|
// Section: api2wire
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
int api2wire_usize(int raw) {
|
int api2wire_u8(int raw) {
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section: finalizer
|
// Section: finalizer
|
||||||
|
|
||||||
class RustPlatform extends FlutterRustBridgeBase<RustWire> {
|
class RustPlatform extends FlutterRustBridgeBase<RustWire> {
|
||||||
@ -113,6 +169,17 @@ class RustPlatform extends FlutterRustBridgeBase<RustWire> {
|
|||||||
|
|
||||||
// Section: api2wire
|
// Section: api2wire
|
||||||
|
|
||||||
|
@protected
|
||||||
|
ffi.Pointer<wire_uint_8_list> api2wire_String(String raw) {
|
||||||
|
return api2wire_uint_8_list(utf8.encoder.convert(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
ffi.Pointer<wire_uint_8_list> api2wire_uint_8_list(Uint8List raw) {
|
||||||
|
final ans = inner.new_uint_8_list_0(raw.length);
|
||||||
|
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
// Section: finalizer
|
// Section: finalizer
|
||||||
|
|
||||||
// Section: api_fill_to_wire
|
// Section: api_fill_to_wire
|
||||||
@ -213,51 +280,77 @@ class RustWire implements FlutterRustBridgeWireBase {
|
|||||||
late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr
|
late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr
|
||||||
.asFunction<int Function(ffi.Pointer<ffi.Void>)>();
|
.asFunction<int Function(ffi.Pointer<ffi.Void>)>();
|
||||||
|
|
||||||
void wire_platform(
|
void wire_ping(
|
||||||
int port_,
|
int port_,
|
||||||
) {
|
) {
|
||||||
return _wire_platform(
|
return _wire_ping(
|
||||||
port_,
|
port_,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _wire_platformPtr =
|
late final _wire_pingPtr =
|
||||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>('wire_ping');
|
||||||
'wire_platform');
|
late final _wire_ping = _wire_pingPtr.asFunction<void Function(int)>();
|
||||||
late final _wire_platform =
|
|
||||||
_wire_platformPtr.asFunction<void Function(int)>();
|
|
||||||
|
|
||||||
void wire_add(
|
void wire_start_download(
|
||||||
int port_,
|
int port_,
|
||||||
int left,
|
ffi.Pointer<wire_uint_8_list> url,
|
||||||
int right,
|
ffi.Pointer<wire_uint_8_list> save_path,
|
||||||
|
ffi.Pointer<wire_uint_8_list> file_name,
|
||||||
|
int connection_count,
|
||||||
) {
|
) {
|
||||||
return _wire_add(
|
return _wire_start_download(
|
||||||
port_,
|
port_,
|
||||||
left,
|
url,
|
||||||
right,
|
save_path,
|
||||||
|
file_name,
|
||||||
|
connection_count,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _wire_addPtr = _lookup<
|
late final _wire_start_downloadPtr = _lookup<
|
||||||
ffi.NativeFunction<
|
ffi.NativeFunction<
|
||||||
ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.UintPtr)>>('wire_add');
|
ffi.Void Function(
|
||||||
late final _wire_add =
|
ffi.Int64,
|
||||||
_wire_addPtr.asFunction<void Function(int, int, int)>();
|
ffi.Pointer<wire_uint_8_list>,
|
||||||
|
ffi.Pointer<wire_uint_8_list>,
|
||||||
|
ffi.Pointer<wire_uint_8_list>,
|
||||||
|
ffi.Uint8)>>('wire_start_download');
|
||||||
|
late final _wire_start_download = _wire_start_downloadPtr.asFunction<
|
||||||
|
void Function(int, ffi.Pointer<wire_uint_8_list>,
|
||||||
|
ffi.Pointer<wire_uint_8_list>, ffi.Pointer<wire_uint_8_list>, int)>();
|
||||||
|
|
||||||
void wire_rust_release_mode(
|
void wire_cancel_download(
|
||||||
int port_,
|
int port_,
|
||||||
|
ffi.Pointer<wire_uint_8_list> id,
|
||||||
) {
|
) {
|
||||||
return _wire_rust_release_mode(
|
return _wire_cancel_download(
|
||||||
port_,
|
port_,
|
||||||
|
id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _wire_rust_release_modePtr =
|
late final _wire_cancel_downloadPtr = _lookup<
|
||||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
ffi.NativeFunction<
|
||||||
'wire_rust_release_mode');
|
ffi.Void Function(ffi.Int64,
|
||||||
late final _wire_rust_release_mode =
|
ffi.Pointer<wire_uint_8_list>)>>('wire_cancel_download');
|
||||||
_wire_rust_release_modePtr.asFunction<void Function(int)>();
|
late final _wire_cancel_download = _wire_cancel_downloadPtr
|
||||||
|
.asFunction<void Function(int, ffi.Pointer<wire_uint_8_list>)>();
|
||||||
|
|
||||||
|
ffi.Pointer<wire_uint_8_list> new_uint_8_list_0(
|
||||||
|
int len,
|
||||||
|
) {
|
||||||
|
return _new_uint_8_list_0(
|
||||||
|
len,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _new_uint_8_list_0Ptr = _lookup<
|
||||||
|
ffi
|
||||||
|
.NativeFunction<ffi.Pointer<wire_uint_8_list> Function(ffi.Int32)>>(
|
||||||
|
'new_uint_8_list_0');
|
||||||
|
late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr
|
||||||
|
.asFunction<ffi.Pointer<wire_uint_8_list> Function(int)>();
|
||||||
|
|
||||||
void free_WireSyncReturn(
|
void free_WireSyncReturn(
|
||||||
WireSyncReturn ptr,
|
WireSyncReturn ptr,
|
||||||
@ -276,6 +369,13 @@ class RustWire implements FlutterRustBridgeWireBase {
|
|||||||
|
|
||||||
final class _Dart_Handle extends ffi.Opaque {}
|
final class _Dart_Handle extends ffi.Opaque {}
|
||||||
|
|
||||||
|
final class wire_uint_8_list extends ffi.Struct {
|
||||||
|
external ffi.Pointer<ffi.Uint8> ptr;
|
||||||
|
|
||||||
|
@ffi.Int32()
|
||||||
|
external int len;
|
||||||
|
}
|
||||||
|
|
||||||
typedef DartPostCObjectFnType = ffi.Pointer<
|
typedef DartPostCObjectFnType = ffi.Pointer<
|
||||||
ffi.NativeFunction<
|
ffi.NativeFunction<
|
||||||
ffi.Bool Function(DartPort port_id, ffi.Pointer<ffi.Void> message)>>;
|
ffi.Bool Function(DartPort port_id, ffi.Pointer<ffi.Void> message)>>;
|
||||||
|
@ -2,7 +2,6 @@ import 'package:desktop_webview_window/desktop_webview_window.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||||
import 'package:starcitizen_doctor/common/conf.dart';
|
import 'package:starcitizen_doctor/common/conf.dart';
|
||||||
import 'package:starcitizen_doctor/common/rust/ffi.dart';
|
|
||||||
import 'package:starcitizen_doctor/ui/index_ui_model.dart';
|
import 'package:starcitizen_doctor/ui/index_ui_model.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@ -10,9 +9,6 @@ import 'global_ui_model.dart';
|
|||||||
import 'ui/index_ui.dart';
|
import 'ui/index_ui.dart';
|
||||||
|
|
||||||
void main(List<String> args) async {
|
void main(List<String> args) async {
|
||||||
|
|
||||||
dPrint("rust ffi ${await rustFii.platform()}");
|
|
||||||
|
|
||||||
if (runWebViewTitleBarWidget(args,
|
if (runWebViewTitleBarWidget(args,
|
||||||
backgroundColor: const Color.fromRGBO(19, 36, 49, 1),
|
backgroundColor: const Color.fromRGBO(19, 36, 49, 1),
|
||||||
builder: _defaultWebviewTitleBar)) {
|
builder: _defaultWebviewTitleBar)) {
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:starcitizen_doctor/base/ui.dart';
|
|
||||||
|
|
||||||
/// https://github.com/qiaoshouqing/dio_range_download/blob/master/lib/dio_range_download.dart
|
|
||||||
|
|
||||||
class RangeDownload {
|
|
||||||
static Future<Response> downloadWithChunks(
|
|
||||||
url,
|
|
||||||
savePath, {
|
|
||||||
bool isRangeDownload = true,
|
|
||||||
ProgressCallback? onReceiveProgress,
|
|
||||||
int maxChunk = 6,
|
|
||||||
Dio? dio,
|
|
||||||
CancelToken? cancelToken,
|
|
||||||
}) async {
|
|
||||||
const firstChunkSize = 102;
|
|
||||||
|
|
||||||
int total = 0;
|
|
||||||
if (dio == null) {
|
|
||||||
dio = Dio();
|
|
||||||
dio.options.connectTimeout = const Duration(seconds: 10);
|
|
||||||
}
|
|
||||||
var progress = <int>[];
|
|
||||||
var progressInit = <int>[];
|
|
||||||
|
|
||||||
Future mergeTempFiles(chunk) async {
|
|
||||||
File f = File(savePath + "temp0");
|
|
||||||
IOSink ioSink = f.openWrite(mode: FileMode.writeOnlyAppend);
|
|
||||||
for (int i = 1; i < chunk; ++i) {
|
|
||||||
File f0 = File(savePath + "temp$i");
|
|
||||||
await ioSink.addStream(f0.openRead());
|
|
||||||
await f0.delete();
|
|
||||||
}
|
|
||||||
await ioSink.close();
|
|
||||||
await f.rename(savePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future mergeFiles(file1, file2, targetFile) async {
|
|
||||||
File f1 = File(file1);
|
|
||||||
File f2 = File(file2);
|
|
||||||
IOSink ioSink = f1.openWrite(mode: FileMode.writeOnlyAppend);
|
|
||||||
await ioSink.addStream(f2.openRead());
|
|
||||||
await f2.delete();
|
|
||||||
await ioSink.close();
|
|
||||||
await f1.rename(targetFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
createCallback(no) {
|
|
||||||
return (int received, rangeTotal) async {
|
|
||||||
if (received >= rangeTotal) {
|
|
||||||
var path = savePath + "temp$no";
|
|
||||||
var oldPath = savePath + "temp${no}_pre";
|
|
||||||
File oldFile = File(oldPath);
|
|
||||||
if (oldFile.existsSync()) {
|
|
||||||
await mergeFiles(oldPath, path, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
progress[no] = progressInit[no] + received;
|
|
||||||
} catch (e) {
|
|
||||||
dPrint(e);
|
|
||||||
}
|
|
||||||
if (onReceiveProgress != null && total != 0) {
|
|
||||||
onReceiveProgress(progress.reduce((a, b) => a + b), total);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Response> downloadChunk(url, start, end, no,
|
|
||||||
{isMerge = true}) async {
|
|
||||||
int initLength = 0;
|
|
||||||
--end;
|
|
||||||
var path = savePath + "temp$no";
|
|
||||||
File targetFile = File(path);
|
|
||||||
if (await targetFile.exists() && isMerge) {
|
|
||||||
dPrint("good job start:$start length:${File(path).lengthSync()}");
|
|
||||||
if (start + await targetFile.length() < end) {
|
|
||||||
initLength = await targetFile.length();
|
|
||||||
start += initLength;
|
|
||||||
var preFile = File(path + "_pre");
|
|
||||||
if (await preFile.exists()) {
|
|
||||||
initLength += await preFile.length();
|
|
||||||
start += await preFile.length();
|
|
||||||
await mergeFiles(preFile.path, targetFile.path, preFile.path);
|
|
||||||
} else {
|
|
||||||
await targetFile.rename(preFile.path);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await targetFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress.add(initLength);
|
|
||||||
progressInit.add(initLength);
|
|
||||||
return dio!.download(
|
|
||||||
url,
|
|
||||||
path,
|
|
||||||
deleteOnError: false,
|
|
||||||
onReceiveProgress: createCallback(no),
|
|
||||||
options: Options(
|
|
||||||
headers: {"range": "bytes=$start-$end"},
|
|
||||||
),
|
|
||||||
cancelToken: cancelToken,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRangeDownload) {
|
|
||||||
Response response =
|
|
||||||
await downloadChunk(url, 0, firstChunkSize, 0, isMerge: false);
|
|
||||||
if (response.statusCode == 206) {
|
|
||||||
dPrint("This http protocol support range download");
|
|
||||||
total = int.parse(response.headers
|
|
||||||
.value(HttpHeaders.contentRangeHeader)!
|
|
||||||
.split("/")
|
|
||||||
.last);
|
|
||||||
int reserved = total -
|
|
||||||
int.parse(response.headers.value(HttpHeaders.contentLengthHeader)!);
|
|
||||||
int chunk = (reserved / firstChunkSize).ceil() + 1;
|
|
||||||
if (chunk > 1) {
|
|
||||||
int chunkSize = firstChunkSize;
|
|
||||||
if (chunk > maxChunk + 1) {
|
|
||||||
chunk = maxChunk + 1;
|
|
||||||
chunkSize = (reserved / maxChunk).ceil();
|
|
||||||
}
|
|
||||||
var futures = <Future>[];
|
|
||||||
for (int i = 0; i < maxChunk; ++i) {
|
|
||||||
int start = firstChunkSize + i * chunkSize;
|
|
||||||
int end;
|
|
||||||
if (i == maxChunk - 1) {
|
|
||||||
end = total;
|
|
||||||
} else {
|
|
||||||
end = start + chunkSize;
|
|
||||||
}
|
|
||||||
futures.add(downloadChunk(url, start, end, i + 1));
|
|
||||||
}
|
|
||||||
await Future.wait(futures);
|
|
||||||
}
|
|
||||||
await mergeTempFiles(chunk);
|
|
||||||
return Response(
|
|
||||||
statusCode: 200,
|
|
||||||
statusMessage: "Download success.",
|
|
||||||
data: "Download success.",
|
|
||||||
requestOptions: RequestOptions(),
|
|
||||||
);
|
|
||||||
} else if (response.statusCode == 200) {
|
|
||||||
dPrint(
|
|
||||||
"The protocol does not support resumed downloads, and regular downloads will be used.");
|
|
||||||
return dio.download(url, savePath,
|
|
||||||
onReceiveProgress: onReceiveProgress,
|
|
||||||
cancelToken: cancelToken,
|
|
||||||
deleteOnError: false);
|
|
||||||
} else {
|
|
||||||
dPrint("The request encountered a problem, please handle it yourself");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return dio.download(url, savePath,
|
|
||||||
onReceiveProgress: onReceiveProgress,
|
|
||||||
cancelToken: cancelToken,
|
|
||||||
deleteOnError: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -54,7 +54,7 @@ class DownloaderDialogUI extends BaseUI<DownloaderDialogUIModel> {
|
|||||||
|
|
||||||
String getStatus(DownloaderDialogUIModel model) {
|
String getStatus(DownloaderDialogUIModel model) {
|
||||||
if (model.progress == null && !model.isInMerging) return "准备中...";
|
if (model.progress == null && !model.isInMerging) return "准备中...";
|
||||||
if (model.isInMerging) return "正在合并文件...";
|
if (model.isInMerging) return "正在处理文件...";
|
||||||
return "${model.progress?.toStringAsFixed(2) ?? "0"}% ";
|
return "${model.progress?.toStringAsFixed(2) ?? "0"}% ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/rust/ffi.dart';
|
||||||
import 'dio_range_download.dart';
|
|
||||||
|
|
||||||
class DownloaderDialogUIModel extends BaseUIModel {
|
class DownloaderDialogUIModel extends BaseUIModel {
|
||||||
final String fileName;
|
final String fileName;
|
||||||
@ -16,16 +14,12 @@ class DownloaderDialogUIModel extends BaseUIModel {
|
|||||||
DownloaderDialogUIModel(this.fileName, this.savePath, this.downloadUrl,
|
DownloaderDialogUIModel(this.fileName, this.savePath, this.downloadUrl,
|
||||||
{this.showChangeSavePathDialog = false, this.threadCount = 1});
|
{this.showChangeSavePathDialog = false, this.threadCount = 1});
|
||||||
|
|
||||||
CancelToken? downloadCancelToken;
|
|
||||||
|
|
||||||
int? downloadTaskId;
|
|
||||||
|
|
||||||
bool isInMerging = false;
|
bool isInMerging = false;
|
||||||
|
|
||||||
|
String? downloadTaskId;
|
||||||
|
|
||||||
double? progress;
|
double? progress;
|
||||||
int? speed;
|
int? speed;
|
||||||
DateTime? lastUpdateTime;
|
|
||||||
int? lastUpdateCount;
|
|
||||||
int? count;
|
int? count;
|
||||||
int? total;
|
int? total;
|
||||||
|
|
||||||
@ -52,48 +46,66 @@ class DownloaderDialogUIModel extends BaseUIModel {
|
|||||||
savePath = userSelect;
|
savePath = userSelect;
|
||||||
dPrint(savePath);
|
dPrint(savePath);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
} else {
|
|
||||||
savePath = "$savePath/$fileName";
|
|
||||||
}
|
}
|
||||||
// start download
|
|
||||||
|
if (savePath.endsWith("\\$fileName")) {
|
||||||
|
savePath = savePath.substring(0, savePath.length - fileName.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
final downloaderSavePath = "$savePath//$fileName.downloading";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
downloadCancelToken = CancelToken();
|
rustFii
|
||||||
final r = await RangeDownload.downloadWithChunks(downloadUrl, savePath,
|
.startDownload(
|
||||||
maxChunk: 10,
|
url: downloadUrl,
|
||||||
cancelToken: downloadCancelToken,
|
savePath: savePath,
|
||||||
isRangeDownload: true, onReceiveProgress: (int count, int total) {
|
fileName: "$fileName.downloading",
|
||||||
lastUpdateTime ??= DateTime.now();
|
connectionCount: 10)
|
||||||
if ((DateTime.now().difference(lastUpdateTime ?? DateTime.now()))
|
.listen((event) async {
|
||||||
.inSeconds >=
|
dPrint(
|
||||||
1) {
|
"id == ${event.id} p ==${event.progress} t==${event.total} s==${event.speed} st==${event.status}");
|
||||||
lastUpdateTime = DateTime.now();
|
|
||||||
speed = (count - (lastUpdateCount ?? 0));
|
downloadTaskId = event.id;
|
||||||
lastUpdateCount = count;
|
count = event.progress;
|
||||||
notifyListeners();
|
if (event.total != 0) {
|
||||||
|
total = event.total;
|
||||||
}
|
}
|
||||||
this.count = count;
|
speed = event.speed;
|
||||||
this.total = total;
|
if (total != null && total != 0 && event.progress != 0) {
|
||||||
progress = count / total * 100;
|
progress = (event.progress / total!) * 100;
|
||||||
if (count == total) {
|
}
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
if (progress != null &&
|
||||||
|
progress != 0 &&
|
||||||
|
event.status == const MyDownloaderStatus.noStart()) {
|
||||||
|
Navigator.pop(context!, "cancel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.status == const MyDownloaderStatus.finished()) {
|
||||||
|
count = total;
|
||||||
isInMerging = true;
|
isInMerging = true;
|
||||||
}
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
await File(downloaderSavePath)
|
||||||
|
.rename(downloaderSavePath.replaceAll(".downloading", ""));
|
||||||
|
final bsonFile = File("$downloaderSavePath.bson");
|
||||||
|
if (await bsonFile.exists()) {
|
||||||
|
bsonFile.delete();
|
||||||
|
}
|
||||||
|
Navigator.pop(context!, "$savePath\\$fileName");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (r.statusCode == 200) {
|
|
||||||
Navigator.pop(context!, savePath);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is DioException && e.type != DioExceptionType.cancel) {
|
|
||||||
Navigator.pop(context!, e);
|
Navigator.pop(context!, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
doCancel() {
|
doCancel() {
|
||||||
try {
|
try {
|
||||||
downloadCancelToken?.cancel();
|
if (downloadTaskId != null) {
|
||||||
downloadCancelToken = null;
|
rustFii.cancelDownload(id: downloadTaskId!);
|
||||||
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
Navigator.pop(context!, "cancel");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ class ToolsUIModel extends BaseUIModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _downloadP4k() async {
|
Future<void> _downloadP4k() async {
|
||||||
final downloadUrl = AppConf.networkVersionData?.p4kDownloadUrl;
|
var downloadUrl = AppConf.networkVersionData?.p4kDownloadUrl;
|
||||||
if (downloadUrl == null || downloadUrl.isEmpty) {
|
if (downloadUrl == null || downloadUrl.isEmpty) {
|
||||||
showToast(context!, "该功能维护中,请稍后再试!");
|
showToast(context!, "该功能维护中,请稍后再试!");
|
||||||
return;
|
return;
|
||||||
|
@ -9,3 +9,8 @@ crate-type = ["staticlib", "cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
flutter_rust_bridge = "1"
|
flutter_rust_bridge = "1"
|
||||||
|
http-downloader = { version = "0.3.2", features = ["status-tracker", "speed-tracker", "breakpoint-resume", "bson-file-archiver"] }
|
||||||
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
url = "2.4.1"
|
||||||
|
uuid = { version = "1.5.0", features = ["v4", "fast-rng", "macro-diagnostics"] }
|
||||||
|
async-std = "1.12.0"
|
||||||
|
@ -1,63 +1,16 @@
|
|||||||
// This is the entry point of your Rust library.
|
use std::sync::Arc;
|
||||||
// When adding new code to your project, note that only items used
|
use async_std::task;
|
||||||
// here will be transformed to their Dart equivalents.
|
use flutter_rust_bridge::StreamSink;
|
||||||
|
use crate::downloader::{do_cancel_download, do_start_download, DownloadCallbackData};
|
||||||
|
|
||||||
// A plain enum without any fields. This is similar to Dart- or C-style enums.
|
pub fn ping() -> String {
|
||||||
// flutter_rust_bridge is capable of generating code for enums with fields
|
return String::from("PONG");
|
||||||
// (@freezed classes in Dart and tagged unions in C).
|
|
||||||
pub enum Platform {
|
|
||||||
Unknown,
|
|
||||||
Android,
|
|
||||||
Ios,
|
|
||||||
Windows,
|
|
||||||
Unix,
|
|
||||||
MacIntel,
|
|
||||||
MacApple,
|
|
||||||
Wasm,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function definition in Rust. Similar to Dart, the return type must always be named
|
pub fn start_download(url: String, save_path: String, file_name: String, connection_count: u8, sink: StreamSink<DownloadCallbackData>) {
|
||||||
// and is never inferred.
|
let _ = do_start_download(url, save_path, file_name, connection_count, Arc::new(sink));
|
||||||
pub fn platform() -> Platform {
|
|
||||||
// This is a macro, a special expression that expands into code. In Rust, all macros
|
|
||||||
// end with an exclamation mark and can be invoked with all kinds of brackets (parentheses,
|
|
||||||
// brackets and curly braces). However, certain conventions exist, for example the
|
|
||||||
// vector macro is almost always invoked as vec![..].
|
|
||||||
//
|
|
||||||
// The cfg!() macro returns a boolean value based on the current compiler configuration.
|
|
||||||
// When attached to expressions (#[cfg(..)] form), they show or hide the expression at compile time.
|
|
||||||
// Here, however, they evaluate to runtime values, which may or may not be optimized out
|
|
||||||
// by the compiler. A variety of configurations are demonstrated here which cover most of
|
|
||||||
// the modern oeprating systems. Try running the Flutter application on different machines
|
|
||||||
// and see if it matches your expected OS.
|
|
||||||
//
|
|
||||||
// Furthermore, in Rust, the last expression in a function is the return value and does
|
|
||||||
// not have the trailing semicolon. This entire if-else chain forms a single expression.
|
|
||||||
if cfg!(windows) {
|
|
||||||
Platform::Windows
|
|
||||||
} else if cfg!(target_os = "android") {
|
|
||||||
Platform::Android
|
|
||||||
} else if cfg!(target_os = "ios") {
|
|
||||||
Platform::Ios
|
|
||||||
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
|
|
||||||
Platform::MacApple
|
|
||||||
} else if cfg!(target_os = "macos") {
|
|
||||||
Platform::MacIntel
|
|
||||||
} else if cfg!(target_family = "wasm") {
|
|
||||||
Platform::Wasm
|
|
||||||
} else if cfg!(unix) {
|
|
||||||
Platform::Unix
|
|
||||||
} else {
|
|
||||||
Platform::Unknown
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
pub fn cancel_download(id: String) {
|
||||||
left + right
|
task::block_on(do_cancel_download(&id))
|
||||||
}
|
|
||||||
|
|
||||||
// The convention for Rust identifiers is the snake_case,
|
|
||||||
// and they are automatically converted to camelCase on the Dart side.
|
|
||||||
pub fn rust_release_mode() -> bool {
|
|
||||||
cfg!(not(debug_assertions))
|
|
||||||
}
|
}
|
@ -2,28 +2,65 @@ use super::*;
|
|||||||
// Section: wire functions
|
// Section: wire functions
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wire_platform(port_: i64) {
|
pub extern "C" fn wire_ping(port_: i64) {
|
||||||
wire_platform_impl(port_)
|
wire_ping_impl(port_)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wire_add(port_: i64, left: usize, right: usize) {
|
pub extern "C" fn wire_start_download(
|
||||||
wire_add_impl(port_, left, right)
|
port_: i64,
|
||||||
|
url: *mut wire_uint_8_list,
|
||||||
|
save_path: *mut wire_uint_8_list,
|
||||||
|
file_name: *mut wire_uint_8_list,
|
||||||
|
connection_count: u8,
|
||||||
|
) {
|
||||||
|
wire_start_download_impl(port_, url, save_path, file_name, connection_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wire_rust_release_mode(port_: i64) {
|
pub extern "C" fn wire_cancel_download(port_: i64, id: *mut wire_uint_8_list) {
|
||||||
wire_rust_release_mode_impl(port_)
|
wire_cancel_download_impl(port_, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section: allocate functions
|
// Section: allocate functions
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list {
|
||||||
|
let ans = wire_uint_8_list {
|
||||||
|
ptr: support::new_leak_vec_ptr(Default::default(), len),
|
||||||
|
len,
|
||||||
|
};
|
||||||
|
support::new_leak_box_ptr(ans)
|
||||||
|
}
|
||||||
|
|
||||||
// Section: related functions
|
// Section: related functions
|
||||||
|
|
||||||
// Section: impl Wire2Api
|
// Section: impl Wire2Api
|
||||||
|
|
||||||
|
impl Wire2Api<String> for *mut wire_uint_8_list {
|
||||||
|
fn wire2api(self) -> String {
|
||||||
|
let vec: Vec<u8> = self.wire2api();
|
||||||
|
String::from_utf8_lossy(&vec).into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wire2Api<Vec<u8>> for *mut wire_uint_8_list {
|
||||||
|
fn wire2api(self) -> Vec<u8> {
|
||||||
|
unsafe {
|
||||||
|
let wrap = support::box_from_leak_ptr(self);
|
||||||
|
support::vec_from_leak_ptr(wrap.ptr, wrap.len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Section: wire structs
|
// Section: wire structs
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wire_uint_8_list {
|
||||||
|
ptr: *mut u8,
|
||||||
|
len: i32,
|
||||||
|
}
|
||||||
|
|
||||||
// Section: impl NewWithNullPtr
|
// Section: impl NewWithNullPtr
|
||||||
|
|
||||||
pub trait NewWithNullPtr {
|
pub trait NewWithNullPtr {
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
// Section: wire functions
|
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn wire_add(port_: i64,left: usize,right: usize) {
|
|
||||||
wire_add_impl(port_,left,right)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Section: allocate functions
|
|
||||||
|
|
||||||
|
|
||||||
// Section: related functions
|
|
||||||
|
|
||||||
|
|
||||||
// Section: impl Wire2Api
|
|
||||||
|
|
||||||
|
|
||||||
// Section: wire structs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: impl NewWithNullPtr
|
|
||||||
|
|
||||||
pub trait NewWithNullPtr {
|
|
||||||
fn new_with_null_ptr() -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> NewWithNullPtr for *mut T {
|
|
||||||
fn new_with_null_ptr() -> Self {
|
|
||||||
std::ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Section: sync execution mode utility
|
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn free_WireSyncReturn(ptr: support::WireSyncReturn) {
|
|
||||||
unsafe { let _ = support::box_from_leak_ptr(ptr); };
|
|
||||||
}
|
|
||||||
|
|
@ -20,46 +20,65 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
// Section: imports
|
// Section: imports
|
||||||
|
|
||||||
|
use crate::downloader::DownloadCallbackData;
|
||||||
|
use crate::downloader::MyDownloaderStatus;
|
||||||
|
use crate::downloader::MyNetworkItemPendingType;
|
||||||
|
|
||||||
// Section: wire functions
|
// Section: wire functions
|
||||||
|
|
||||||
fn wire_platform_impl(port_: MessagePort) {
|
fn wire_ping_impl(port_: MessagePort) {
|
||||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Platform, _>(
|
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String, _>(
|
||||||
WrapInfo {
|
WrapInfo {
|
||||||
debug_name: "platform",
|
debug_name: "ping",
|
||||||
port: Some(port_),
|
port: Some(port_),
|
||||||
mode: FfiCallMode::Normal,
|
mode: FfiCallMode::Normal,
|
||||||
},
|
},
|
||||||
move || move |task_callback| Result::<_, ()>::Ok(platform()),
|
move || move |task_callback| Result::<_, ()>::Ok(ping()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn wire_add_impl(
|
fn wire_start_download_impl(
|
||||||
port_: MessagePort,
|
port_: MessagePort,
|
||||||
left: impl Wire2Api<usize> + UnwindSafe,
|
url: impl Wire2Api<String> + UnwindSafe,
|
||||||
right: impl Wire2Api<usize> + UnwindSafe,
|
save_path: impl Wire2Api<String> + UnwindSafe,
|
||||||
|
file_name: impl Wire2Api<String> + UnwindSafe,
|
||||||
|
connection_count: impl Wire2Api<u8> + UnwindSafe,
|
||||||
) {
|
) {
|
||||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, usize, _>(
|
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, (), _>(
|
||||||
WrapInfo {
|
WrapInfo {
|
||||||
debug_name: "add",
|
debug_name: "start_download",
|
||||||
|
port: Some(port_),
|
||||||
|
mode: FfiCallMode::Stream,
|
||||||
|
},
|
||||||
|
move || {
|
||||||
|
let api_url = url.wire2api();
|
||||||
|
let api_save_path = save_path.wire2api();
|
||||||
|
let api_file_name = file_name.wire2api();
|
||||||
|
let api_connection_count = connection_count.wire2api();
|
||||||
|
move |task_callback| {
|
||||||
|
Result::<_, ()>::Ok(start_download(
|
||||||
|
api_url,
|
||||||
|
api_save_path,
|
||||||
|
api_file_name,
|
||||||
|
api_connection_count,
|
||||||
|
task_callback.stream_sink::<_, DownloadCallbackData>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn wire_cancel_download_impl(port_: MessagePort, id: impl Wire2Api<String> + UnwindSafe) {
|
||||||
|
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, (), _>(
|
||||||
|
WrapInfo {
|
||||||
|
debug_name: "cancel_download",
|
||||||
port: Some(port_),
|
port: Some(port_),
|
||||||
mode: FfiCallMode::Normal,
|
mode: FfiCallMode::Normal,
|
||||||
},
|
},
|
||||||
move || {
|
move || {
|
||||||
let api_left = left.wire2api();
|
let api_id = id.wire2api();
|
||||||
let api_right = right.wire2api();
|
move |task_callback| Result::<_, ()>::Ok(cancel_download(api_id))
|
||||||
move |task_callback| Result::<_, ()>::Ok(add(api_left, api_right))
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn wire_rust_release_mode_impl(port_: MessagePort) {
|
|
||||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, bool, _>(
|
|
||||||
WrapInfo {
|
|
||||||
debug_name: "rust_release_mode",
|
|
||||||
port: Some(port_),
|
|
||||||
mode: FfiCallMode::Normal,
|
|
||||||
},
|
|
||||||
move || move |task_callback| Result::<_, ()>::Ok(rust_release_mode()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Section: wrapper structs
|
// Section: wrapper structs
|
||||||
|
|
||||||
// Section: static checks
|
// Section: static checks
|
||||||
@ -82,30 +101,66 @@ where
|
|||||||
(!self.is_null()).then(|| self.wire2api())
|
(!self.is_null()).then(|| self.wire2api())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Wire2Api<usize> for usize {
|
|
||||||
fn wire2api(self) -> usize {
|
impl Wire2Api<u8> for u8 {
|
||||||
|
fn wire2api(self) -> u8 {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section: impl IntoDart
|
// Section: impl IntoDart
|
||||||
|
|
||||||
impl support::IntoDart for Platform {
|
impl support::IntoDart for DownloadCallbackData {
|
||||||
|
fn into_dart(self) -> support::DartAbi {
|
||||||
|
vec![
|
||||||
|
self.id.into_into_dart().into_dart(),
|
||||||
|
self.total.into_into_dart().into_dart(),
|
||||||
|
self.progress.into_into_dart().into_dart(),
|
||||||
|
self.speed.into_into_dart().into_dart(),
|
||||||
|
self.status.into_into_dart().into_dart(),
|
||||||
|
]
|
||||||
|
.into_dart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl support::IntoDartExceptPrimitive for DownloadCallbackData {}
|
||||||
|
impl rust2dart::IntoIntoDart<DownloadCallbackData> for DownloadCallbackData {
|
||||||
|
fn into_into_dart(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl support::IntoDart for MyDownloaderStatus {
|
||||||
fn into_dart(self) -> support::DartAbi {
|
fn into_dart(self) -> support::DartAbi {
|
||||||
match self {
|
match self {
|
||||||
Self::Unknown => 0,
|
Self::NoStart => vec![0.into_dart()],
|
||||||
Self::Android => 1,
|
Self::Running => vec![1.into_dart()],
|
||||||
Self::Ios => 2,
|
Self::Pending(field0) => vec![2.into_dart(), field0.into_into_dart().into_dart()],
|
||||||
Self::Windows => 3,
|
Self::Error(field0) => vec![3.into_dart(), field0.into_into_dart().into_dart()],
|
||||||
Self::Unix => 4,
|
Self::Finished => vec![4.into_dart()],
|
||||||
Self::MacIntel => 5,
|
|
||||||
Self::MacApple => 6,
|
|
||||||
Self::Wasm => 7,
|
|
||||||
}
|
}
|
||||||
.into_dart()
|
.into_dart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl support::IntoDartExceptPrimitive for Platform {}
|
impl support::IntoDartExceptPrimitive for MyDownloaderStatus {}
|
||||||
impl rust2dart::IntoIntoDart<Platform> for Platform {
|
impl rust2dart::IntoIntoDart<MyDownloaderStatus> for MyDownloaderStatus {
|
||||||
|
fn into_into_dart(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl support::IntoDart for MyNetworkItemPendingType {
|
||||||
|
fn into_dart(self) -> support::DartAbi {
|
||||||
|
match self {
|
||||||
|
Self::QueueUp => 0,
|
||||||
|
Self::Starting => 1,
|
||||||
|
Self::Stopping => 2,
|
||||||
|
Self::Initializing => 3,
|
||||||
|
}
|
||||||
|
.into_dart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl support::IntoDartExceptPrimitive for MyNetworkItemPendingType {}
|
||||||
|
impl rust2dart::IntoIntoDart<MyNetworkItemPendingType> for MyNetworkItemPendingType {
|
||||||
fn into_into_dart(self) -> Self {
|
fn into_into_dart(self) -> Self {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
183
rust/src/downloader/mod.rs
Normal file
183
rust/src/downloader/mod.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::num::{NonZeroU8, NonZeroUsize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
use async_std::sync::Mutex;
|
||||||
|
use http_downloader::{breakpoint_resume::DownloadBreakpointResumeExtension, ExtendedHttpFileDownloader, HttpDownloaderBuilder, speed_tracker::DownloadSpeedTrackerExtension, status_tracker::DownloadStatusTrackerExtension};
|
||||||
|
use http_downloader::bson_file_archiver::{ArchiveFilePath, BsonFileArchiverBuilder};
|
||||||
|
use url::Url;
|
||||||
|
use flutter_rust_bridge::StreamSink;
|
||||||
|
use flutter_rust_bridge::support::lazy_static;
|
||||||
|
use http_downloader::status_tracker::{DownloaderStatus, NetworkItemPendingType};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub enum MyNetworkItemPendingType {
|
||||||
|
QueueUp,
|
||||||
|
Starting,
|
||||||
|
Stopping,
|
||||||
|
Initializing,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MyDownloaderStatus {
|
||||||
|
NoStart,
|
||||||
|
Running,
|
||||||
|
Pending(MyNetworkItemPendingType),
|
||||||
|
Error(String),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DownloadCallbackData {
|
||||||
|
pub id: String,
|
||||||
|
pub total: u64,
|
||||||
|
pub progress: u64,
|
||||||
|
pub speed: u64,
|
||||||
|
pub status: MyDownloaderStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DownloadCallbackData {
|
||||||
|
pub fn new(id: String, total: u64) -> Self {
|
||||||
|
DownloadCallbackData {
|
||||||
|
id,
|
||||||
|
total,
|
||||||
|
progress: 0,
|
||||||
|
speed: 0,
|
||||||
|
status: MyDownloaderStatus::NoStart,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DOWNLOADERS_MAP: Mutex<HashMap<String, Arc<ExtendedHttpFileDownloader>>> = {
|
||||||
|
let map = HashMap::new();
|
||||||
|
Mutex::new(map)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn do_cancel_download(id: &str) {
|
||||||
|
let d = get_downloader(id).await;
|
||||||
|
if d.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.unwrap().cancel().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn do_start_download(url: String, save_path: String, file_name: String, connection_count: u8, sink: Arc<StreamSink<DownloadCallbackData>>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let save_dir = PathBuf::from(save_path);
|
||||||
|
let test_url = Url::parse(&*url)?;
|
||||||
|
|
||||||
|
let (mut downloader, (status_state, speed_state, ..)) =
|
||||||
|
|
||||||
|
HttpDownloaderBuilder::new(test_url, save_dir)
|
||||||
|
.chunk_size(NonZeroUsize::new(1024 * 1024 * 10).unwrap()) // 块大小
|
||||||
|
.download_connection_count(NonZeroU8::new(connection_count).unwrap())
|
||||||
|
.file_name(Option::from(file_name))
|
||||||
|
.build((
|
||||||
|
// 下载状态追踪扩展
|
||||||
|
// by cargo feature "status-tracker" enable
|
||||||
|
DownloadStatusTrackerExtension { log: true },
|
||||||
|
// 下载速度追踪扩展
|
||||||
|
// by cargo feature "speed-tracker" enable
|
||||||
|
DownloadSpeedTrackerExtension { log: true },
|
||||||
|
// 断点续传扩展,
|
||||||
|
// by cargo feature "breakpoint-resume" enable
|
||||||
|
DownloadBreakpointResumeExtension {
|
||||||
|
// BsonFileArchiver by cargo feature "bson-file-archiver" enable
|
||||||
|
download_archiver_builder: BsonFileArchiverBuilder::new(ArchiveFilePath::Suffix("bson".to_string()))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let status_state_arc = Arc::new(status_state);
|
||||||
|
let status_state_clone = Arc::clone(&status_state_arc);
|
||||||
|
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
// info!("Prepare download,准备下载");
|
||||||
|
let download_future = downloader.prepare_download()?;
|
||||||
|
let sink_clone = sink.clone();
|
||||||
|
add_downloader(&id.to_string(), Arc::new(downloader)).await;
|
||||||
|
|
||||||
|
// 打印下载进度
|
||||||
|
// Print download Progress
|
||||||
|
tokio::spawn({
|
||||||
|
let mut downloaded_len_receiver = get_downloader(&id.to_string()).await.unwrap().downloaded_len_receiver().clone();
|
||||||
|
let total_size_future = get_downloader(&id.to_string()).await.unwrap().total_size_future();
|
||||||
|
async move {
|
||||||
|
let total_len = total_size_future.await;
|
||||||
|
if let Some(total_len) = total_len {
|
||||||
|
// info!("Total size: {:.2} Mb",total_len.get() as f64 / 1024_f64/ 1024_f64);
|
||||||
|
sink_clone.add(DownloadCallbackData::new(id.to_string(), total_len.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
while downloaded_len_receiver.changed().await.is_ok() {
|
||||||
|
let p = *downloaded_len_receiver.borrow();
|
||||||
|
let _status = status_state_clone.status(); // get download status, 获取状态
|
||||||
|
let _byte_per_second = speed_state.download_speed(); // get download speed,Byte per second,获取速度,字节每秒
|
||||||
|
|
||||||
|
if let Some(total_len) = total_len {
|
||||||
|
sink_clone.add(DownloadCallbackData {
|
||||||
|
id: id.to_string(),
|
||||||
|
total: total_len.get(),
|
||||||
|
progress: p,
|
||||||
|
speed: _byte_per_second,
|
||||||
|
status: get_my_status(_status),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
download_future.await?;
|
||||||
|
let _status = status_state_arc.status();
|
||||||
|
sink.add(DownloadCallbackData {
|
||||||
|
id: id.to_string(),
|
||||||
|
total: 0,
|
||||||
|
progress: 0,
|
||||||
|
speed: 0,
|
||||||
|
status: get_my_status(_status),
|
||||||
|
});
|
||||||
|
sink.close();
|
||||||
|
remove_downloader(&id.to_string()).await;
|
||||||
|
println!("rust downloader download complete");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_downloader(id: &str) {
|
||||||
|
let mut downloader_map = DOWNLOADERS_MAP.lock().await;
|
||||||
|
downloader_map.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_downloader(id: &str) -> Option<Arc<ExtendedHttpFileDownloader>> {
|
||||||
|
let downloader_map = DOWNLOADERS_MAP.lock().await;
|
||||||
|
return if let Some(downloader) = downloader_map.get(id) {
|
||||||
|
Some(downloader.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_downloader(id: &str, d: Arc<ExtendedHttpFileDownloader>) {
|
||||||
|
let mut downloader_map = DOWNLOADERS_MAP.lock().await;
|
||||||
|
downloader_map.insert(id.to_string(), d);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_my_status(_status: DownloaderStatus) -> MyDownloaderStatus {
|
||||||
|
match _status {
|
||||||
|
DownloaderStatus::NoStart => { MyDownloaderStatus::NoStart }
|
||||||
|
DownloaderStatus::Running => { MyDownloaderStatus::Running }
|
||||||
|
DownloaderStatus::Pending(n) => { MyDownloaderStatus::Pending(get_my_network_type(n)) }
|
||||||
|
DownloaderStatus::Error(e) => { MyDownloaderStatus::Error(e) }
|
||||||
|
DownloaderStatus::Finished => { MyDownloaderStatus::Finished }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_my_network_type(n: NetworkItemPendingType) -> MyNetworkItemPendingType {
|
||||||
|
match n {
|
||||||
|
NetworkItemPendingType::QueueUp => { MyNetworkItemPendingType::QueueUp }
|
||||||
|
NetworkItemPendingType::Starting => { MyNetworkItemPendingType::Starting }
|
||||||
|
NetworkItemPendingType::Stopping => { MyNetworkItemPendingType::Stopping }
|
||||||
|
NetworkItemPendingType::Initializing => { MyNetworkItemPendingType::Initializing }
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
mod api;
|
mod api;
|
||||||
mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */
|
mod bridge_generated;
|
||||||
|
mod downloader;
|
||||||
|
Loading…
Reference in New Issue
Block a user