feat: add batched Rust-backed upload task
CI / format (push) Has been cancelled
CI / test (push) Has been cancelled
CI / packaging (push) Has been cancelled

This commit is contained in:
Tien Do Nam
2026-07-15 18:18:58 +02:00
parent 3bb7627646
commit e9acf98be3
15 changed files with 722 additions and 129 deletions
@@ -1,4 +1,3 @@
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:localsend_app/isolate/isolate.dart'; import 'package:localsend_app/isolate/isolate.dart';
@@ -6,8 +5,8 @@ import 'package:localsend_app/isolate/model/device.dart';
import 'package:localsend_app/isolate/src/isolate/child/main.dart'; import 'package:localsend_app/isolate/src/isolate/child/main.dart';
import 'package:localsend_app/isolate/src/isolate/dto/send_to_isolate_data.dart'; import 'package:localsend_app/isolate/src/isolate/dto/send_to_isolate_data.dart';
import 'package:localsend_app/isolate/src/task/upload/http_upload.dart'; import 'package:localsend_app/isolate/src/task/upload/http_upload.dart';
import 'package:localsend_app/isolate/util/stream.dart';
import 'package:localsend_app/rust/api/http.dart'; import 'package:localsend_app/rust/api/http.dart';
import 'package:localsend_app/util/native/channel/android_channel.dart';
import 'package:localsend_app/util/rust.dart'; import 'package:localsend_app/util/rust.dart';
import 'package:refena_flutter/refena_flutter.dart'; import 'package:refena_flutter/refena_flutter.dart';
import 'package:typed_isolates/typed_isolates.dart'; import 'package:typed_isolates/typed_isolates.dart';
@@ -42,6 +41,39 @@ class HttpUploadTask implements BaseHttpUploadTask {
}); });
} }
class HttpUploadFile {
final String remoteFileToken;
final String fileId;
final String? filePath;
final List<int>? fileBytes;
final int fileSize;
HttpUploadFile({
required this.remoteFileToken,
required this.fileId,
required this.filePath,
required this.fileBytes,
required this.fileSize,
});
}
/// Uploads a list of files as one isolate task.
///
/// This task is intended to replace the file scheduling loop in the parent
/// isolate. Files are uploaded sequentially and progress is reported across
/// the complete list.
class HttpUploadFilesTask implements BaseHttpUploadTask {
final String? remoteSessionId;
final List<HttpUploadFile> files;
final Device device;
HttpUploadFilesTask({
required this.remoteSessionId,
required this.files,
required this.device,
});
}
class HttpUploadCancelTask implements BaseHttpUploadTask { class HttpUploadCancelTask implements BaseHttpUploadTask {
final int taskId; final int taskId;
@@ -61,8 +93,6 @@ abstract class UriContentStreamResolver {
Stream<Uint8List> resolve(Uri uri); Stream<Uint8List> resolve(Uri uri);
} }
UriContentStreamResolver? _uriContentStreamResolver;
Future<void> setupHttpUploadIsolate( Future<void> setupHttpUploadIsolate(
Stream<SendToIsolateData<IsolateTask<BaseHttpUploadTask>>> receiveFromMain, Stream<SendToIsolateData<IsolateTask<BaseHttpUploadTask>>> receiveFromMain,
void Function(IsolateTaskStreamResult<double>) sendToMain, void Function(IsolateTaskStreamResult<double>) sendToMain,
@@ -74,17 +104,33 @@ Future<void> setupHttpUploadIsolate(
sendToMain: sendToMain, sendToMain: sendToMain,
initialData: initialData, initialData: initialData,
handler: (ref, task) async { handler: (ref, task) async {
final HttpUploadTask uploadTask; final String? remoteSessionId;
final List<HttpUploadFile> files;
final Device device;
switch (task.data) { switch (task.data) {
case HttpUploadSetContentStreamResolverTask task: case HttpUploadSetContentStreamResolverTask task:
final rootIsolateToken = ref.read(syncProvider).rootIsolateToken; final rootIsolateToken = ref.read(syncProvider).rootIsolateToken;
task.resolver.init( task.resolver.init(
rootIsolateToken: rootIsolateToken, rootIsolateToken: rootIsolateToken,
); );
_uriContentStreamResolver = task.resolver;
return; return;
case HttpUploadTask task: case HttpUploadTask task:
uploadTask = task; remoteSessionId = task.remoteSessionId;
files = [
HttpUploadFile(
remoteFileToken: task.remoteFileToken,
fileId: task.fileId,
filePath: task.filePath,
fileBytes: task.fileBytes,
fileSize: task.fileSize,
),
];
device = task.device;
break;
case HttpUploadFilesTask task:
remoteSessionId = task.remoteSessionId;
files = task.files;
device = task.device;
break; break;
case HttpUploadCancelTask task: case HttpUploadCancelTask task:
final cancelToken = ref.read(_cancelTokenProvider)[task.taskId]; final cancelToken = ref.read(_cancelTokenProvider)[task.taskId];
@@ -93,37 +139,42 @@ Future<void> setupHttpUploadIsolate(
return; return;
} }
final Stream<List<int>>? fileStream = uploadTask.filePath != null
? _uriContentStreamResolver != null && uploadTask.filePath!.startsWith('content://')
? _uriContentStreamResolver!.resolve(Uri.parse(uploadTask.filePath!))
: File(uploadTask.filePath!).openRead()
: null;
final (streamController, subscription) = fileStream?.digested() ?? (null, null);
try { try {
final cancelToken = createCancellationToken(); final cancelToken = createCancellationToken();
ref.read(_cancelTokenProvider).putIfAbsent(task.id, () => cancelToken); ref.read(_cancelTokenProvider).putIfAbsent(task.id, () => cancelToken);
final totalSize = files.fold<int>(0, (sum, file) => sum + file.fileSize);
var completedSize = 0;
await ref for (final file in files) {
.read(httpUploadProvider) final filePath = file.filePath;
.upload( final isContentUri = filePath?.startsWith('content://') ?? false;
stream: streamController?.stream ?? Stream.fromIterable([uploadTask.fileBytes!]), final fileDescriptor = isContentUri ? await getFileDescriptorAndroid(uri: filePath!) : null;
contentLength: uploadTask.fileSize,
target: uploadTask.device, await ref
remoteSessionId: uploadTask.remoteSessionId, .read(httpUploadProvider)
fileId: uploadTask.fileId, .upload(
token: uploadTask.remoteFileToken, stream: filePath == null && file.fileBytes != null ? Stream.value(file.fileBytes!) : null,
onSendProgress: (progress) { path: !isContentUri ? filePath : null,
sendToMain( fileDescriptor: fileDescriptor,
IsolateTaskStreamResult.event( contentLength: file.fileSize,
id: task.id, target: device,
data: progress, remoteSessionId: remoteSessionId,
), fileId: file.fileId,
); token: file.remoteFileToken,
}, onSendProgress: (progress) {
cancelToken: cancelToken, final totalProgress = totalSize == 0 ? 1.0 : (completedSize + progress * file.fileSize) / totalSize;
); sendToMain(
IsolateTaskStreamResult.event(
id: task.id,
data: totalProgress,
),
);
},
cancelToken: cancelToken,
);
completedSize += file.fileSize;
}
sendToMain( sendToMain(
IsolateTaskStreamResult.done( IsolateTaskStreamResult.done(
@@ -138,13 +189,7 @@ Future<void> setupHttpUploadIsolate(
), ),
); );
} finally { } finally {
// Close the stream if it is still open ref.read(_cancelTokenProvider).remove(task.id);
// ignore: unawaited_futures
streamController?.close();
// Cancel the subscription if it is still open
// ignore: unawaited_futures
subscription?.cancel();
} }
}, },
); );
@@ -172,6 +172,42 @@ class IsolateHttpUploadAction extends ReduxActionWithResult<IsolateController, P
} }
} }
class IsolateHttpUploadFilesAction extends ReduxActionWithResult<IsolateController, ParentIsolateState, IsolateHttpUploadActionResult> {
final int isolateIndex;
final String? remoteSessionId;
final List<HttpUploadFile> files;
final Device device;
IsolateHttpUploadFilesAction({
required this.isolateIndex,
required this.remoteSessionId,
required this.files,
required this.device,
});
@override
(ParentIsolateState, IsolateHttpUploadActionResult) reduce() {
final connection = state.httpUpload[isolateIndex];
final taskId = IdProvider.instance.getNextId();
final progress = connection.sendWrappedTaskAndListenStream(
task: HttpUploadFilesTask(
remoteSessionId: remoteSessionId,
files: files,
device: device,
),
taskId: taskId,
);
return (
state,
IsolateHttpUploadActionResult(
taskId: taskId,
progress: progress,
),
);
}
}
class IsolateHttpUploadCancelAction extends ReduxAction<IsolateController, ParentIsolateState> { class IsolateHttpUploadCancelAction extends ReduxAction<IsolateController, ParentIsolateState> {
final int isolateIndex; final int isolateIndex;
final int taskId; final int taskId;
@@ -16,7 +16,9 @@ class HttpUploadService {
HttpUploadService(this._client); HttpUploadService(this._client);
Future<void> upload({ Future<void> upload({
required Stream<List<int>> stream, required Stream<List<int>>? stream,
required String? path,
required int? fileDescriptor,
required int contentLength, required int contentLength,
required Device target, required Device target,
required String? remoteSessionId, required String? remoteSessionId,
@@ -25,25 +27,29 @@ class HttpUploadService {
required void Function(double progress) onSendProgress, required void Function(double progress) onSendProgress,
required RsCancellationToken cancelToken, required RsCancellationToken cancelToken,
}) async { }) async {
final (sink, receiver) = await createStream(); final (sink, receiver) = stream != null ? await createStream() : (null, null);
final uploadFuture = _client.upload( final uploadFuture = _client
protocol: target.getProtocolType(), .upload(
ip: target.ip!, protocol: target.getProtocolType(),
port: target.port, ip: target.ip!,
publicKey: null, port: target.port,
sessionId: remoteSessionId ?? '', publicKey: null,
fileId: fileId, sessionId: remoteSessionId ?? '',
token: token, fileId: fileId,
binary: receiver, token: token,
cancelToken: cancelToken, binary: receiver,
); path: path,
fileDescriptor: fileDescriptor,
contentLength: BigInt.from(contentLength),
cancelToken: cancelToken,
)
.forEach(onSendProgress);
try { try {
int sent = 0; await for (final chunk in stream ?? const Stream<List<int>>.empty()) {
await for (final chunk in stream) {
try { try {
await sink.add(data: chunk); await sink!.add(data: chunk);
} catch (_) { } catch (_) {
// The Rust side dropped the receiver, i.e. the upload request already // The Rust side dropped the receiver, i.e. the upload request already
// ended (e.g. rejected by the receiver or cancelled). // ended (e.g. rejected by the receiver or cancelled).
@@ -51,10 +57,8 @@ class HttpUploadService {
await uploadFuture; await uploadFuture;
rethrow; rethrow;
} }
sent += chunk.length;
onSendProgress(sent / contentLength);
} }
sink.close(); sink?.close();
} catch (e) { } catch (e) {
// The source stream failed, so the upload request must be aborted. // The source stream failed, so the upload request must be aborted.
// [e] is the root cause, thus the error of the upload request is swallowed. // [e] is the root cause, thus the error of the upload request is swallowed.
+6 -2
View File
@@ -11,6 +11,7 @@ import 'package:localsend_app/rust/frb_generated.dart';
part 'http.freezed.dart'; part 'http.freezed.dart';
// These functions are ignored because they are not marked as `pub`: `resolve_file_content`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `from` // These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `from`
RsHttpClient createClient({ RsHttpClient createClient({
@@ -57,7 +58,7 @@ abstract class RsHttpClient implements RustOpaqueInterface {
required RegisterDto payload, required RegisterDto payload,
}); });
Future<void> upload({ Stream<double> upload({
required ProtocolType protocol, required ProtocolType protocol,
required String ip, required String ip,
required int port, required int port,
@@ -65,7 +66,10 @@ abstract class RsHttpClient implements RustOpaqueInterface {
required String sessionId, required String sessionId,
required String fileId, required String fileId,
required String token, required String token,
required Dart2RustStreamReceiver binary, Dart2RustStreamReceiver? binary,
String? path,
int? fileDescriptor,
required BigInt contentLength,
required RsCancellationToken cancelToken, required RsCancellationToken cancelToken,
}); });
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import 'package:localsend_app/rust/frb_generated.dart';
Future<(Dart2RustStreamSink, Dart2RustStreamReceiver)> createStream() => RustLib.instance.api.crateApiStreamCreateStream(); Future<(Dart2RustStreamSink, Dart2RustStreamReceiver)> createStream() => RustLib.instance.api.crateApiStreamCreateStream();
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamReceiver>> // Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner< Dart2RustStreamReceiver>>
abstract class Dart2RustStreamReceiver implements RustOpaqueInterface {} abstract class Dart2RustStreamReceiver implements RustOpaqueInterface {}
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamSink>> // Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamSink>>
+249 -50
View File
@@ -145,7 +145,7 @@ abstract class RustLibApi extends BaseApi {
required RegisterDto payload, required RegisterDto payload,
}); });
Future<void> crateApiHttpRsHttpClientUpload({ Stream<double> crateApiHttpRsHttpClientUpload({
required RsHttpClient that, required RsHttpClient that,
required ProtocolType protocol, required ProtocolType protocol,
required String ip, required String ip,
@@ -154,7 +154,10 @@ abstract class RustLibApi extends BaseApi {
required String sessionId, required String sessionId,
required String fileId, required String fileId,
required String token, required String token,
required Dart2RustStreamReceiver binary, Dart2RustStreamReceiver? binary,
String? path,
int? fileDescriptor,
required BigInt contentLength,
required RsCancellationToken cancelToken, required RsCancellationToken cancelToken,
}); });
@@ -725,7 +728,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
); );
@override @override
Future<void> crateApiHttpRsHttpClientUpload({ Stream<double> crateApiHttpRsHttpClientUpload({
required RsHttpClient that, required RsHttpClient that,
required ProtocolType protocol, required ProtocolType protocol,
required String ip, required String ip,
@@ -734,65 +737,81 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
required String sessionId, required String sessionId,
required String fileId, required String fileId,
required String token, required String token,
required Dart2RustStreamReceiver binary, Dart2RustStreamReceiver? binary,
String? path,
int? fileDescriptor,
required BigInt contentLength,
required RsCancellationToken cancelToken, required RsCancellationToken cancelToken,
}) { }) {
return handler.executeNormal( final sink = RustStreamSink<double>();
NormalTask( unawaited(
callFfi: (port_) { handler.executeNormal(
final serializer = SseSerializer(generalizedFrbRustBinding); NormalTask(
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpClient( callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsHttpClient(
that,
serializer,
);
sse_encode_StreamSink_f_64_Sse(sink, serializer);
sse_encode_protocol_type(protocol, serializer);
sse_encode_String(ip, serializer);
sse_encode_u_16(port, serializer);
sse_encode_opt_String(publicKey, serializer);
sse_encode_String(sessionId, serializer);
sse_encode_String(fileId, serializer);
sse_encode_String(token, serializer);
sse_encode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
binary,
serializer,
);
sse_encode_opt_String(path, serializer);
sse_encode_opt_box_autoadd_i_32(fileDescriptor, serializer);
sse_encode_u_64(contentLength, serializer);
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsCancellationToken(
cancelToken,
serializer,
);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 10,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_rs_http_client_error,
),
constMeta: kCrateApiHttpRsHttpClientUploadConstMeta,
argValues: [
that, that,
serializer, sink,
); protocol,
sse_encode_protocol_type(protocol, serializer); ip,
sse_encode_String(ip, serializer); port,
sse_encode_u_16(port, serializer); publicKey,
sse_encode_opt_String(publicKey, serializer); sessionId,
sse_encode_String(sessionId, serializer); fileId,
sse_encode_String(fileId, serializer); token,
sse_encode_String(token, serializer);
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
binary, binary,
serializer, path,
); fileDescriptor,
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerRsCancellationToken( contentLength,
cancelToken, cancelToken,
serializer, ],
); apiImpl: this,
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 10,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_rs_http_client_error,
), ),
constMeta: kCrateApiHttpRsHttpClientUploadConstMeta,
argValues: [
that,
protocol,
ip,
port,
publicKey,
sessionId,
fileId,
token,
binary,
cancelToken,
],
apiImpl: this,
), ),
); );
return sink.stream;
} }
TaskConstMeta get kCrateApiHttpRsHttpClientUploadConstMeta => const TaskConstMeta( TaskConstMeta get kCrateApiHttpRsHttpClientUploadConstMeta => const TaskConstMeta(
debugName: 'RsHttpClient_upload', debugName: 'RsHttpClient_upload',
argNames: [ argNames: [
'that', 'that',
'sink',
'protocol', 'protocol',
'ip', 'ip',
'port', 'port',
@@ -801,6 +820,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
'fileId', 'fileId',
'token', 'token',
'binary', 'binary',
'path',
'fileDescriptor',
'contentLength',
'cancelToken', 'cancelToken',
], ],
); );
@@ -2001,6 +2023,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
throw UnimplementedError(); throw UnimplementedError();
} }
@protected
RustStreamSink<double> dco_decode_StreamSink_f_64_Sse(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
throw UnimplementedError();
}
@protected @protected
RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse(
dynamic raw, dynamic raw,
@@ -2049,6 +2077,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw as bool; return raw as bool;
} }
@protected
Dart2RustStreamReceiver dco_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
raw,
);
}
@protected @protected
ClientInfo dco_decode_box_autoadd_client_info(dynamic raw) { ClientInfo dco_decode_box_autoadd_client_info(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2081,6 +2119,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return dco_decode_file_metadata(raw); return dco_decode_file_metadata(raw);
} }
@protected
int dco_decode_box_autoadd_i_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as int;
}
@protected @protected
PinConfig dco_decode_box_autoadd_pin_config(dynamic raw) { PinConfig dco_decode_box_autoadd_pin_config(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2183,6 +2227,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
); );
} }
@protected
double dco_decode_f_64(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as double;
}
@protected @protected
FileDto dco_decode_file_dto(dynamic raw) { FileDto dco_decode_file_dto(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2287,6 +2337,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw == null ? null : dco_decode_String(raw); return raw == null ? null : dco_decode_String(raw);
} }
@protected
Dart2RustStreamReceiver?
dco_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw == null
? null
: dco_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
raw,
);
}
@protected @protected
DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw) { DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2307,6 +2370,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return raw == null ? null : dco_decode_box_autoadd_file_metadata(raw); return raw == null ? null : dco_decode_box_autoadd_file_metadata(raw);
} }
@protected
int? dco_decode_opt_box_autoadd_i_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw == null ? null : dco_decode_box_autoadd_i_32(raw);
}
@protected @protected
PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw) { PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs // Codec=Dco (DartCObject based), see doc to use other codecs
@@ -2987,6 +3056,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
throw UnimplementedError('Unreachable ()'); throw UnimplementedError('Unreachable ()');
} }
@protected
RustStreamSink<double> sse_decode_StreamSink_f_64_Sse(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
throw UnimplementedError('Unreachable ()');
}
@protected @protected
RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -3039,6 +3116,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return deserializer.buffer.getUint8() != 0; return deserializer.buffer.getUint8() != 0;
} }
@protected
Dart2RustStreamReceiver sse_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
return (sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
deserializer,
));
}
@protected @protected
ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer) { ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -3075,6 +3162,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return (sse_decode_file_metadata(deserializer)); return (sse_decode_file_metadata(deserializer));
} }
@protected
int sse_decode_box_autoadd_i_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return (sse_decode_i_32(deserializer));
}
@protected @protected
PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer) { PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -3190,6 +3283,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return ExpectingPublicKey(publicKey: var_publicKey, kind: var_kind); return ExpectingPublicKey(publicKey: var_publicKey, kind: var_kind);
} }
@protected
double sse_decode_f_64(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getFloat64();
}
@protected @protected
FileDto sse_decode_file_dto(SseDeserializer deserializer) { FileDto sse_decode_file_dto(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -3337,6 +3436,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
} }
} }
@protected
Dart2RustStreamReceiver?
sse_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
if (sse_decode_bool(deserializer)) {
return (sse_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
deserializer,
));
} else {
return null;
}
}
@protected @protected
DeviceType? sse_decode_opt_box_autoadd_device_type( DeviceType? sse_decode_opt_box_autoadd_device_type(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -3376,6 +3491,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
} }
} }
@protected
int? sse_decode_opt_box_autoadd_i_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
if (sse_decode_bool(deserializer)) {
return (sse_decode_box_autoadd_i_32(deserializer));
} else {
return null;
}
}
@protected @protected
PinConfig? sse_decode_opt_box_autoadd_pin_config( PinConfig? sse_decode_opt_box_autoadd_pin_config(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -4152,6 +4278,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
); );
} }
@protected
void sse_encode_StreamSink_f_64_Sse(
RustStreamSink<double> self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(
self.setupAndSerialize(
codec: SseCodec(
decodeSuccessData: sse_decode_f_64,
decodeErrorData: sse_decode_AnyhowException,
),
),
serializer,
);
}
@protected @protected
void sse_encode_StreamSink_list_prim_u_8_strict_Sse( void sse_encode_StreamSink_list_prim_u_8_strict_Sse(
RustStreamSink<Uint8List> self, RustStreamSink<Uint8List> self,
@@ -4238,6 +4381,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer.buffer.putUint8(self ? 1 : 0); serializer.buffer.putUint8(self ? 1 : 0);
} }
@protected
void sse_encode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
self,
serializer,
);
}
@protected @protected
void sse_encode_box_autoadd_client_info( void sse_encode_box_autoadd_client_info(
ClientInfo self, ClientInfo self,
@@ -4283,6 +4438,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_file_metadata(self, serializer); sse_encode_file_metadata(self, serializer);
} }
@protected
void sse_encode_box_autoadd_i_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self, serializer);
}
@protected @protected
void sse_encode_box_autoadd_pin_config( void sse_encode_box_autoadd_pin_config(
PinConfig self, PinConfig self,
@@ -4392,6 +4553,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(self.kind, serializer); sse_encode_String(self.kind, serializer);
} }
@protected
void sse_encode_f_64(double self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putFloat64(self);
}
@protected @protected
void sse_encode_file_dto(FileDto self, SseSerializer serializer) { void sse_encode_file_dto(FileDto self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
@@ -4525,6 +4692,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
} }
} }
@protected
void sse_encode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver? self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_bool(self != null, serializer);
if (self != null) {
sse_encode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
self,
serializer,
);
}
}
@protected @protected
void sse_encode_opt_box_autoadd_device_type( void sse_encode_opt_box_autoadd_device_type(
DeviceType? self, DeviceType? self,
@@ -4564,6 +4747,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
} }
} }
@protected
void sse_encode_opt_box_autoadd_i_32(int? self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_bool(self != null, serializer);
if (self != null) {
sse_encode_box_autoadd_i_32(self, serializer);
}
}
@protected @protected
void sse_encode_opt_box_autoadd_pin_config( void sse_encode_opt_box_autoadd_pin_config(
PinConfig? self, PinConfig? self,
@@ -5057,7 +5250,7 @@ class RsHttpClientImpl extends RustOpaque implements RsHttpClient {
payload: payload, payload: payload,
); );
Future<void> upload({ Stream<double> upload({
required ProtocolType protocol, required ProtocolType protocol,
required String ip, required String ip,
required int port, required int port,
@@ -5065,7 +5258,10 @@ class RsHttpClientImpl extends RustOpaque implements RsHttpClient {
required String sessionId, required String sessionId,
required String fileId, required String fileId,
required String token, required String token,
required Dart2RustStreamReceiver binary, Dart2RustStreamReceiver? binary,
String? path,
int? fileDescriptor,
required BigInt contentLength,
required RsCancellationToken cancelToken, required RsCancellationToken cancelToken,
}) => RustLib.instance.api.crateApiHttpRsHttpClientUpload( }) => RustLib.instance.api.crateApiHttpRsHttpClientUpload(
that: this, that: this,
@@ -5077,6 +5273,9 @@ class RsHttpClientImpl extends RustOpaque implements RsHttpClient {
fileId: fileId, fileId: fileId,
token: token, token: token,
binary: binary, binary: binary,
path: path,
fileDescriptor: fileDescriptor,
contentLength: contentLength,
cancelToken: cancelToken, cancelToken: cancelToken,
); );
} }
+75
View File
@@ -213,6 +213,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
dynamic raw, dynamic raw,
); );
@protected
RustStreamSink<double> dco_decode_StreamSink_f_64_Sse(dynamic raw);
@protected @protected
RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse(
dynamic raw, dynamic raw,
@@ -240,6 +243,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
bool dco_decode_bool(dynamic raw); bool dco_decode_bool(dynamic raw);
@protected
Dart2RustStreamReceiver dco_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
);
@protected @protected
ClientInfo dco_decode_box_autoadd_client_info(dynamic raw); ClientInfo dco_decode_box_autoadd_client_info(dynamic raw);
@@ -257,6 +265,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
FileMetadata dco_decode_box_autoadd_file_metadata(dynamic raw); FileMetadata dco_decode_box_autoadd_file_metadata(dynamic raw);
@protected
int dco_decode_box_autoadd_i_32(dynamic raw);
@protected @protected
PinConfig dco_decode_box_autoadd_pin_config(dynamic raw); PinConfig dco_decode_box_autoadd_pin_config(dynamic raw);
@@ -299,6 +310,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
ExpectingPublicKey dco_decode_expecting_public_key(dynamic raw); ExpectingPublicKey dco_decode_expecting_public_key(dynamic raw);
@protected
double dco_decode_f_64(dynamic raw);
@protected @protected
FileDto dco_decode_file_dto(dynamic raw); FileDto dco_decode_file_dto(dynamic raw);
@@ -341,6 +355,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
String? dco_decode_opt_String(dynamic raw); String? dco_decode_opt_String(dynamic raw);
@protected
Dart2RustStreamReceiver?
dco_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
);
@protected @protected
DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw); DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw);
@@ -352,6 +372,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
FileMetadata? dco_decode_opt_box_autoadd_file_metadata(dynamic raw); FileMetadata? dco_decode_opt_box_autoadd_file_metadata(dynamic raw);
@protected
int? dco_decode_opt_box_autoadd_i_32(dynamic raw);
@protected @protected
PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw); PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw);
@@ -595,6 +618,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
RustStreamSink<double> sse_decode_StreamSink_f_64_Sse(
SseDeserializer deserializer,
);
@protected @protected
RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -624,6 +652,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
bool sse_decode_bool(SseDeserializer deserializer); bool sse_decode_bool(SseDeserializer deserializer);
@protected
Dart2RustStreamReceiver sse_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
);
@protected @protected
ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer); ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer);
@@ -645,6 +678,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
int sse_decode_box_autoadd_i_32(SseDeserializer deserializer);
@protected @protected
PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer); PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer);
@@ -695,6 +731,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
double sse_decode_f_64(SseDeserializer deserializer);
@protected @protected
FileDto sse_decode_file_dto(SseDeserializer deserializer); FileDto sse_decode_file_dto(SseDeserializer deserializer);
@@ -743,6 +782,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
String? sse_decode_opt_String(SseDeserializer deserializer); String? sse_decode_opt_String(SseDeserializer deserializer);
@protected
Dart2RustStreamReceiver?
sse_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
);
@protected @protected
DeviceType? sse_decode_opt_box_autoadd_device_type( DeviceType? sse_decode_opt_box_autoadd_device_type(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -758,6 +803,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
int? sse_decode_opt_box_autoadd_i_32(SseDeserializer deserializer);
@protected @protected
PinConfig? sse_decode_opt_box_autoadd_pin_config( PinConfig? sse_decode_opt_box_autoadd_pin_config(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -1067,6 +1115,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_StreamSink_f_64_Sse(
RustStreamSink<double> self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_StreamSink_list_prim_u_8_strict_Sse( void sse_encode_StreamSink_list_prim_u_8_strict_Sse(
RustStreamSink<Uint8List> self, RustStreamSink<Uint8List> self,
@@ -1100,6 +1154,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
void sse_encode_bool(bool self, SseSerializer serializer); void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_box_autoadd_client_info( void sse_encode_box_autoadd_client_info(
ClientInfo self, ClientInfo self,
@@ -1130,6 +1190,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_box_autoadd_i_32(int self, SseSerializer serializer);
@protected @protected
void sse_encode_box_autoadd_pin_config( void sse_encode_box_autoadd_pin_config(
PinConfig self, PinConfig self,
@@ -1193,6 +1256,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_f_64(double self, SseSerializer serializer);
@protected @protected
void sse_encode_file_dto(FileDto self, SseSerializer serializer); void sse_encode_file_dto(FileDto self, SseSerializer serializer);
@@ -1250,6 +1316,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
void sse_encode_opt_String(String? self, SseSerializer serializer); void sse_encode_opt_String(String? self, SseSerializer serializer);
@protected
void sse_encode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver? self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_opt_box_autoadd_device_type( void sse_encode_opt_box_autoadd_device_type(
DeviceType? self, DeviceType? self,
@@ -1268,6 +1340,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_opt_box_autoadd_i_32(int? self, SseSerializer serializer);
@protected @protected
void sse_encode_opt_box_autoadd_pin_config( void sse_encode_opt_box_autoadd_pin_config(
PinConfig? self, PinConfig? self,
+75
View File
@@ -215,6 +215,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
dynamic raw, dynamic raw,
); );
@protected
RustStreamSink<double> dco_decode_StreamSink_f_64_Sse(dynamic raw);
@protected @protected
RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> dco_decode_StreamSink_list_prim_u_8_strict_Sse(
dynamic raw, dynamic raw,
@@ -242,6 +245,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
bool dco_decode_bool(dynamic raw); bool dco_decode_bool(dynamic raw);
@protected
Dart2RustStreamReceiver dco_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
);
@protected @protected
ClientInfo dco_decode_box_autoadd_client_info(dynamic raw); ClientInfo dco_decode_box_autoadd_client_info(dynamic raw);
@@ -259,6 +267,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
FileMetadata dco_decode_box_autoadd_file_metadata(dynamic raw); FileMetadata dco_decode_box_autoadd_file_metadata(dynamic raw);
@protected
int dco_decode_box_autoadd_i_32(dynamic raw);
@protected @protected
PinConfig dco_decode_box_autoadd_pin_config(dynamic raw); PinConfig dco_decode_box_autoadd_pin_config(dynamic raw);
@@ -301,6 +312,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
ExpectingPublicKey dco_decode_expecting_public_key(dynamic raw); ExpectingPublicKey dco_decode_expecting_public_key(dynamic raw);
@protected
double dco_decode_f_64(dynamic raw);
@protected @protected
FileDto dco_decode_file_dto(dynamic raw); FileDto dco_decode_file_dto(dynamic raw);
@@ -343,6 +357,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
String? dco_decode_opt_String(dynamic raw); String? dco_decode_opt_String(dynamic raw);
@protected
Dart2RustStreamReceiver?
dco_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
dynamic raw,
);
@protected @protected
DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw); DeviceType? dco_decode_opt_box_autoadd_device_type(dynamic raw);
@@ -354,6 +374,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
FileMetadata? dco_decode_opt_box_autoadd_file_metadata(dynamic raw); FileMetadata? dco_decode_opt_box_autoadd_file_metadata(dynamic raw);
@protected
int? dco_decode_opt_box_autoadd_i_32(dynamic raw);
@protected @protected
PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw); PinConfig? dco_decode_opt_box_autoadd_pin_config(dynamic raw);
@@ -597,6 +620,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
RustStreamSink<double> sse_decode_StreamSink_f_64_Sse(
SseDeserializer deserializer,
);
@protected @protected
RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse( RustStreamSink<Uint8List> sse_decode_StreamSink_list_prim_u_8_strict_Sse(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -626,6 +654,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
bool sse_decode_bool(SseDeserializer deserializer); bool sse_decode_bool(SseDeserializer deserializer);
@protected
Dart2RustStreamReceiver sse_decode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
);
@protected @protected
ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer); ClientInfo sse_decode_box_autoadd_client_info(SseDeserializer deserializer);
@@ -647,6 +680,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
int sse_decode_box_autoadd_i_32(SseDeserializer deserializer);
@protected @protected
PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer); PinConfig sse_decode_box_autoadd_pin_config(SseDeserializer deserializer);
@@ -697,6 +733,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
double sse_decode_f_64(SseDeserializer deserializer);
@protected @protected
FileDto sse_decode_file_dto(SseDeserializer deserializer); FileDto sse_decode_file_dto(SseDeserializer deserializer);
@@ -745,6 +784,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
String? sse_decode_opt_String(SseDeserializer deserializer); String? sse_decode_opt_String(SseDeserializer deserializer);
@protected
Dart2RustStreamReceiver?
sse_decode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
SseDeserializer deserializer,
);
@protected @protected
DeviceType? sse_decode_opt_box_autoadd_device_type( DeviceType? sse_decode_opt_box_autoadd_device_type(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -760,6 +805,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseDeserializer deserializer, SseDeserializer deserializer,
); );
@protected
int? sse_decode_opt_box_autoadd_i_32(SseDeserializer deserializer);
@protected @protected
PinConfig? sse_decode_opt_box_autoadd_pin_config( PinConfig? sse_decode_opt_box_autoadd_pin_config(
SseDeserializer deserializer, SseDeserializer deserializer,
@@ -1069,6 +1117,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_StreamSink_f_64_Sse(
RustStreamSink<double> self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_StreamSink_list_prim_u_8_strict_Sse( void sse_encode_StreamSink_list_prim_u_8_strict_Sse(
RustStreamSink<Uint8List> self, RustStreamSink<Uint8List> self,
@@ -1102,6 +1156,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
void sse_encode_bool(bool self, SseSerializer serializer); void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_box_autoadd_client_info( void sse_encode_box_autoadd_client_info(
ClientInfo self, ClientInfo self,
@@ -1132,6 +1192,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_box_autoadd_i_32(int self, SseSerializer serializer);
@protected @protected
void sse_encode_box_autoadd_pin_config( void sse_encode_box_autoadd_pin_config(
PinConfig self, PinConfig self,
@@ -1195,6 +1258,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_f_64(double self, SseSerializer serializer);
@protected @protected
void sse_encode_file_dto(FileDto self, SseSerializer serializer); void sse_encode_file_dto(FileDto self, SseSerializer serializer);
@@ -1252,6 +1318,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected @protected
void sse_encode_opt_String(String? self, SseSerializer serializer); void sse_encode_opt_String(String? self, SseSerializer serializer);
@protected
void sse_encode_opt_box_autoadd_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
Dart2RustStreamReceiver? self,
SseSerializer serializer,
);
@protected @protected
void sse_encode_opt_box_autoadd_device_type( void sse_encode_opt_box_autoadd_device_type(
DeviceType? self, DeviceType? self,
@@ -1270,6 +1342,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SseSerializer serializer, SseSerializer serializer,
); );
@protected
void sse_encode_opt_box_autoadd_i_32(int? self, SseSerializer serializer);
@protected @protected
void sse_encode_opt_box_autoadd_pin_config( void sse_encode_opt_box_autoadd_pin_config(
PinConfig? self, PinConfig? self,
+47 -2
View File
@@ -1,4 +1,5 @@
use crate::api::stream; use crate::api::stream;
use crate::frb_generated::StreamSink;
use flutter_rust_bridge::frb; use flutter_rust_bridge::frb;
pub use localsend::http::client::{ClientError, LsHttpClientVersion}; pub use localsend::http::client::{ClientError, LsHttpClientVersion};
pub use localsend::http::dto::{ pub use localsend::http::dto::{
@@ -86,6 +87,7 @@ impl RsHttpClient {
pub async fn upload( pub async fn upload(
&self, &self,
sink: StreamSink<f64>,
protocol: ProtocolType, protocol: ProtocolType,
ip: &str, ip: &str,
port: u16, port: u16,
@@ -93,9 +95,22 @@ impl RsHttpClient {
session_id: &str, session_id: &str,
file_id: &str, file_id: &str,
token: &str, token: &str,
binary: stream::Dart2RustStreamReceiver, binary: Option<stream::Dart2RustStreamReceiver>,
path: Option<String>,
file_descriptor: Option<i32>,
content_length: u64,
cancel_token: &RsCancellationToken, cancel_token: &RsCancellationToken,
) -> Result<(), RsHttpClientError> { ) -> Result<(), RsHttpClientError> {
let content = resolve_file_content(binary, path, file_descriptor)?;
let progress = move |sent| {
let progress = if content_length == 0 {
1.0
} else {
(sent as f64 / content_length as f64).min(1.0)
};
let _ = sink.add(progress);
};
self.inner self.inner
.upload( .upload(
protocol, protocol,
@@ -105,7 +120,8 @@ impl RsHttpClient {
session_id, session_id,
file_id, file_id,
token, token,
localsend::model::transfer::FileContent::Stream(binary.receiver), content,
progress,
cancel_token.inner.clone(), cancel_token.inner.clone(),
) )
.await .await
@@ -130,6 +146,35 @@ impl RsHttpClient {
} }
} }
fn resolve_file_content(
binary: Option<stream::Dart2RustStreamReceiver>,
path: Option<String>,
file_descriptor: Option<i32>,
) -> Result<localsend::model::transfer::FileContent, RsHttpClientError> {
match (binary, path, file_descriptor) {
(Some(binary), None, None) => Ok(localsend::model::transfer::FileContent::Stream(
binary.receiver,
)),
(None, Some(path), None) => Ok(localsend::model::transfer::FileContent::Path(path.into())),
(None, None, Some(file_descriptor)) => {
#[cfg(target_os = "android")]
{
Ok(localsend::model::transfer::FileContent::Fd(file_descriptor))
}
#[cfg(not(target_os = "android"))]
{
let _ = file_descriptor;
Err(RsHttpClientError::Other(
"File descriptors are only supported on Android".into(),
))
}
}
_ => Err(RsHttpClientError::Other(
"Exactly one upload content source must be provided".into(),
)),
}
}
pub enum RsHttpClientError { pub enum RsHttpClientError {
StatusCode { StatusCode {
status: u16, status: u16,
+87 -5
View File
@@ -633,6 +633,10 @@ fn wire__crate__api__http__RsHttpClient_upload_impl(
let api_that = <RustOpaqueMoi< let api_that = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpClient>, flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsHttpClient>,
>>::sse_decode(&mut deserializer); >>::sse_decode(&mut deserializer);
let api_sink =
<StreamSink<f64, flutter_rust_bridge::for_generated::SseCodec>>::sse_decode(
&mut deserializer,
);
let api_protocol = <crate::api::model::ProtocolType>::sse_decode(&mut deserializer); let api_protocol = <crate::api::model::ProtocolType>::sse_decode(&mut deserializer);
let api_ip = <String>::sse_decode(&mut deserializer); let api_ip = <String>::sse_decode(&mut deserializer);
let api_port = <u16>::sse_decode(&mut deserializer); let api_port = <u16>::sse_decode(&mut deserializer);
@@ -640,7 +644,10 @@ fn wire__crate__api__http__RsHttpClient_upload_impl(
let api_session_id = <String>::sse_decode(&mut deserializer); let api_session_id = <String>::sse_decode(&mut deserializer);
let api_file_id = <String>::sse_decode(&mut deserializer); let api_file_id = <String>::sse_decode(&mut deserializer);
let api_token = <String>::sse_decode(&mut deserializer); let api_token = <String>::sse_decode(&mut deserializer);
let api_binary = <Dart2RustStreamReceiver>::sse_decode(&mut deserializer); let api_binary = <Option<Dart2RustStreamReceiver>>::sse_decode(&mut deserializer);
let api_path = <Option<String>>::sse_decode(&mut deserializer);
let api_file_descriptor = <Option<i32>>::sse_decode(&mut deserializer);
let api_content_length = <u64>::sse_decode(&mut deserializer);
let api_cancel_token = <RustOpaqueMoi< let api_cancel_token = <RustOpaqueMoi<
flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsCancellationToken>, flutter_rust_bridge::for_generated::RustAutoOpaqueInner<RsCancellationToken>,
>>::sse_decode(&mut deserializer); >>::sse_decode(&mut deserializer);
@@ -680,6 +687,7 @@ fn wire__crate__api__http__RsHttpClient_upload_impl(
let api_cancel_token_guard = api_cancel_token_guard.unwrap(); let api_cancel_token_guard = api_cancel_token_guard.unwrap();
let output_ok = crate::api::http::RsHttpClient::upload( let output_ok = crate::api::http::RsHttpClient::upload(
&*api_that_guard, &*api_that_guard,
api_sink,
api_protocol, api_protocol,
&api_ip, &api_ip,
api_port, api_port,
@@ -688,6 +696,9 @@ fn wire__crate__api__http__RsHttpClient_upload_impl(
&api_file_id, &api_file_id,
&api_token, &api_token,
api_binary, api_binary,
api_path,
api_file_descriptor,
api_content_length,
&*api_cancel_token_guard, &*api_cancel_token_guard,
) )
.await?; .await?;
@@ -2323,6 +2334,14 @@ impl SseDecode for StreamSink<RTCFileReceiver, flutter_rust_bridge::for_generate
} }
} }
impl SseDecode for StreamSink<f64, flutter_rust_bridge::for_generated::SseCodec> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut inner = <String>::sse_decode(deserializer);
return StreamSink::deserialize(inner);
}
}
impl SseDecode for StreamSink<Vec<u8>, flutter_rust_bridge::for_generated::SseCodec> { impl SseDecode for StreamSink<Vec<u8>, flutter_rust_bridge::for_generated::SseCodec> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -2452,6 +2471,13 @@ impl SseDecode for crate::api::webrtc::ExpectingPublicKey {
} }
} }
impl SseDecode for f64 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_f64::<NativeEndian>().unwrap()
}
}
impl SseDecode for crate::api::model::FileDto { impl SseDecode for crate::api::model::FileDto {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -2609,6 +2635,17 @@ impl SseDecode for Option<String> {
} }
} }
impl SseDecode for Option<Dart2RustStreamReceiver> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
if (<bool>::sse_decode(deserializer)) {
return Some(<Dart2RustStreamReceiver>::sse_decode(deserializer));
} else {
return None;
}
}
}
impl SseDecode for Option<crate::api::model::DeviceType> { impl SseDecode for Option<crate::api::model::DeviceType> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -2644,6 +2681,17 @@ impl SseDecode for Option<crate::api::model::FileMetadata> {
} }
} }
impl SseDecode for Option<i32> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
if (<bool>::sse_decode(deserializer)) {
return Some(<i32>::sse_decode(deserializer));
} else {
return None;
}
}
}
impl SseDecode for Option<crate::api::webrtc::PinConfig> { impl SseDecode for Option<crate::api::webrtc::PinConfig> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -4147,6 +4195,13 @@ impl SseEncode for StreamSink<RTCFileReceiver, flutter_rust_bridge::for_generate
} }
} }
impl SseEncode for StreamSink<f64, flutter_rust_bridge::for_generated::SseCodec> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
unimplemented!("")
}
}
impl SseEncode for StreamSink<Vec<u8>, flutter_rust_bridge::for_generated::SseCodec> { impl SseEncode for StreamSink<Vec<u8>, flutter_rust_bridge::for_generated::SseCodec> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -4255,6 +4310,13 @@ impl SseEncode for crate::api::webrtc::ExpectingPublicKey {
} }
} }
impl SseEncode for f64 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_f64::<NativeEndian>(self).unwrap();
}
}
impl SseEncode for crate::api::model::FileDto { impl SseEncode for crate::api::model::FileDto {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -4387,6 +4449,16 @@ impl SseEncode for Option<String> {
} }
} }
impl SseEncode for Option<Dart2RustStreamReceiver> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<bool>::sse_encode(self.is_some(), serializer);
if let Some(value) = self {
<Dart2RustStreamReceiver>::sse_encode(value, serializer);
}
}
}
impl SseEncode for Option<crate::api::model::DeviceType> { impl SseEncode for Option<crate::api::model::DeviceType> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -4417,6 +4489,16 @@ impl SseEncode for Option<crate::api::model::FileMetadata> {
} }
} }
impl SseEncode for Option<i32> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<bool>::sse_encode(self.is_some(), serializer);
if let Some(value) = self {
<i32>::sse_encode(value, serializer);
}
}
}
impl SseEncode for Option<crate::api::webrtc::PinConfig> { impl SseEncode for Option<crate::api::webrtc::PinConfig> {
// Codec=Sse (Serialization based), see doc to use other codecs // Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
@@ -4770,14 +4852,14 @@ mod io {
pub extern "C" fn frbgen_localsend_app_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver( pub extern "C" fn frbgen_localsend_app_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
ptr: *const std::ffi::c_void, ptr: *const std::ffi::c_void,
) { ) {
MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamReceiver>>::increment_strong_count(ptr as _); MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner< Dart2RustStreamReceiver>>::increment_strong_count(ptr as _);
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn frbgen_localsend_app_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver( pub extern "C" fn frbgen_localsend_app_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
ptr: *const std::ffi::c_void, ptr: *const std::ffi::c_void,
) { ) {
MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamReceiver>>::decrement_strong_count(ptr as _); MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner< Dart2RustStreamReceiver>>::decrement_strong_count(ptr as _);
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@@ -4923,14 +5005,14 @@ mod web {
pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver( pub fn rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
ptr: *const std::ffi::c_void, ptr: *const std::ffi::c_void,
) { ) {
MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamReceiver>>::increment_strong_count(ptr as _); MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner< Dart2RustStreamReceiver>>::increment_strong_count(ptr as _);
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver( pub fn rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerDart2RustStreamReceiver(
ptr: *const std::ffi::c_void, ptr: *const std::ffi::c_void,
) { ) {
MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Dart2RustStreamReceiver>>::decrement_strong_count(ptr as _); MoiArc::<flutter_rust_bridge::for_generated::RustAutoOpaqueInner< Dart2RustStreamReceiver>>::decrement_strong_count(ptr as _);
} }
#[wasm_bindgen] #[wasm_bindgen]
+1 -1
View File
@@ -6,9 +6,9 @@
import 'dart:async' as _i4; import 'dart:async' as _i4;
import 'package:flutter/material.dart' as _i8; import 'package:flutter/material.dart' as _i8;
import 'package:localsend_app/gen/strings.g.dart' as _i10;
import 'package:localsend_app/isolate/model/device.dart' as _i12; import 'package:localsend_app/isolate/model/device.dart' as _i12;
import 'package:localsend_app/isolate/model/stored_security_context.dart' as _i2; import 'package:localsend_app/isolate/model/stored_security_context.dart' as _i2;
import 'package:localsend_app/gen/strings.g.dart' as _i10;
import 'package:localsend_app/model/persistence/color_mode.dart' as _i9; import 'package:localsend_app/model/persistence/color_mode.dart' as _i9;
import 'package:localsend_app/model/persistence/favorite_device.dart' as _i6; import 'package:localsend_app/model/persistence/favorite_device.dart' as _i6;
import 'package:localsend_app/model/persistence/receive_history_entry.dart' as _i5; import 'package:localsend_app/model/persistence/receive_history_entry.dart' as _i5;
+5 -2
View File
@@ -114,20 +114,23 @@ impl LsHttpClient {
file_id: &str, file_id: &str,
token: &str, token: &str,
content: model::transfer::FileContent, content: model::transfer::FileContent,
progress: impl Fn(u64) + Send + 'static,
cancel: tokio_util::sync::CancellationToken, cancel: tokio_util::sync::CancellationToken,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
match self { match self {
LsHttpClient::V2(client) => { LsHttpClient::V2(client) => {
client client
.upload( .upload(
protocol, ip, port, public_key, session_id, file_id, token, content, cancel, protocol, ip, port, public_key, session_id, file_id, token, content,
progress, cancel,
) )
.await .await
} }
LsHttpClient::V3(client) => { LsHttpClient::V3(client) => {
client client
.upload( .upload(
protocol, ip, port, public_key, session_id, file_id, token, content, cancel, protocol, ip, port, public_key, session_id, file_id, token, content,
progress, cancel,
) )
.await .await
} }
+8 -1
View File
@@ -200,6 +200,7 @@ impl LsHttpClientV2 {
/// * `file_id` - File ID to upload /// * `file_id` - File ID to upload
/// * `token` - File-specific token from prepare_upload /// * `token` - File-specific token from prepare_upload
/// * `content` - The file content to upload (a chunk stream or a raw file descriptor) /// * `content` - The file content to upload (a chunk stream or a raw file descriptor)
/// * `progress` - Called with the cumulative number of bytes read for the upload
/// * `cancel` - Cancellation token; cancelling it aborts the upload with [`ClientError::Cancelled`] /// * `cancel` - Cancellation token; cancelling it aborts the upload with [`ClientError::Cancelled`]
/// ///
/// # Errors /// # Errors
@@ -217,6 +218,7 @@ impl LsHttpClientV2 {
file_id: &str, file_id: &str,
token: &str, token: &str,
content: model::transfer::FileContent, content: model::transfer::FileContent,
progress: impl Fn(u64) + Send + 'static,
cancel: CancellationToken, cancel: CancellationToken,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let url = TargetUrl { let url = TargetUrl {
@@ -233,7 +235,12 @@ impl LsHttpClientV2 {
} }
.to_string(); .to_string();
let stream = ReceiverStream::new(content.into_receiver()).map(Ok::<Bytes, anyhow::Error>); let mut sent = 0_u64;
let stream = ReceiverStream::new(content.into_receiver()).map(move |chunk| {
sent += chunk.len() as u64;
progress(sent);
Ok::<Bytes, anyhow::Error>(chunk)
});
let body = reqwest::Body::wrap_stream(stream); let body = reqwest::Body::wrap_stream(stream);
let res = tokio::select! { let res = tokio::select! {
+9 -2
View File
@@ -187,6 +187,8 @@ impl LsHttpClientV3 {
/// Uploads a file to the server. /// Uploads a file to the server.
/// ///
/// `progress` is called with the cumulative number of bytes read for the upload.
///
/// `cancel` is a cancellation token; cancelling it aborts the upload with /// `cancel` is a cancellation token; cancelling it aborts the upload with
/// [`ClientError::Cancelled`]. /// [`ClientError::Cancelled`].
pub async fn upload( pub async fn upload(
@@ -199,6 +201,7 @@ impl LsHttpClientV3 {
file_id: &str, file_id: &str,
token: &str, token: &str,
content: model::transfer::FileContent, content: model::transfer::FileContent,
progress: impl Fn(u64) + Send + 'static,
cancel: CancellationToken, cancel: CancellationToken,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let send = self let send = self
@@ -219,8 +222,12 @@ impl LsHttpClientV3 {
.to_string(), .to_string(),
) )
.body({ .body({
let stream = let mut sent = 0_u64;
ReceiverStream::new(content.into_receiver()).map(Ok::<Bytes, anyhow::Error>); let stream = ReceiverStream::new(content.into_receiver()).map(move |chunk| {
sent += chunk.len() as u64;
progress(sent);
Ok::<Bytes, anyhow::Error>(chunk)
});
reqwest::Body::wrap_stream(stream) reqwest::Body::wrap_stream(stream)
}) })
.send(); .send();
+14 -3
View File
@@ -11,7 +11,7 @@ use localsend::http::state::ClientInfo;
use localsend::model::transfer::{FileContent, FileDto}; use localsend::model::transfer::{FileContent, FileDto};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::atomic::{AtomicU16, AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{mpsc, oneshot, Mutex}; use tokio::sync::{mpsc, oneshot, Mutex};
@@ -209,6 +209,7 @@ async fn upload_bytes(
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let (tx, rx) = mpsc::channel::<Bytes>(4); let (tx, rx) = mpsc::channel::<Bytes>(4);
let chunks: Vec<Vec<u8>> = bytes.chunks(1024).map(|chunk| chunk.to_vec()).collect(); let chunks: Vec<Vec<u8>> = bytes.chunks(1024).map(|chunk| chunk.to_vec()).collect();
let sent = Arc::new(AtomicU64::new(0));
tokio::spawn(async move { tokio::spawn(async move {
for chunk in chunks { for chunk in chunks {
if tx.send(Bytes::from(chunk)).await.is_err() { if tx.send(Bytes::from(chunk)).await.is_err() {
@@ -217,7 +218,8 @@ async fn upload_bytes(
} }
}); });
client let progress = sent.clone();
let result = client
.upload( .upload(
ProtocolType::Http, ProtocolType::Http,
"127.0.0.1", "127.0.0.1",
@@ -227,9 +229,18 @@ async fn upload_bytes(
file_id, file_id,
token, token,
FileContent::Stream(rx), FileContent::Stream(rx),
move |bytes_sent| {
progress.store(bytes_sent, Ordering::Relaxed);
},
CancellationToken::new(), CancellationToken::new(),
) )
.await .await;
if result.is_ok() {
assert_eq!(sent.load(Ordering::Relaxed), bytes.len() as u64);
}
result
} }
fn assert_status(result: Result<impl Sized, ClientError>, expected_status: u16) { fn assert_status(result: Result<impl Sized, ClientError>, expected_status: u16) {