diff --git a/.devcontainer/Containerfile b/.devcontainer/Containerfile index 7476f77..0d3c5da 100644 --- a/.devcontainer/Containerfile +++ b/.devcontainer/Containerfile @@ -25,10 +25,17 @@ ENV VERSION=$VERSION \ # Install linux dependency and utils RUN set -eux; mkdir -p /usr/lib $PUB_CACHE \ && apt-get update \ - && apt-get install -y openjdk-17-jdk \ + && apt-get install -y locales openjdk-17-jdk \ && rm -rf /var/lib/apt/lists/* /var/cache/apk/* \ && mkdir -p ${ANDROID_HOME}/cmdline-tools /root/.android +# Set the locale +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ + locale-gen +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + # Install & config Flutter RUN set -eux; git clone -b ${VERSION} --depth 1 "${FLUTTER_URL}.git" "${FLUTTER_ROOT}" \ && cd "${FLUTTER_ROOT}" \ @@ -65,10 +72,7 @@ RUN set -eux; cd "${FLUTTER_HOME}/bin" \ RUN set -eux; apt-get update && apt-get install -y libclang-dev \ && cargo install just \ cargo-ndk \ - flutter_rust_bridge_codegen \ - # we dont really need this because we dont use macros in our rust code but for - # now there is no option to disable cargo expand in flutter_rust_bridge_codegen - cargo-expand \ + 'flutter_rust_bridge_codegen@^2.0.0-dev.3' \ && rustup component add rustfmt \ && rustup target add \ aarch64-linux-android \ diff --git a/justfile b/justfile index 5d31e5b..c9d92ab 100644 --- a/justfile +++ b/justfile @@ -18,9 +18,6 @@ clean: build: #!{{shebang}} cd src - flutter pub get - flutter_rust_bridge_codegen --rust-input ./rust/src/api.rs --dart-output ./lib/models/webp_bridge_generated.dart - cd rust; cargo ndk --platform 24 -t armeabi-v7a -t arm64-v8a -o ../android/app/src/main/jniLibs build --release flutter build apk rebuild: clean build \ No newline at end of file diff --git a/src/android/build.gradle b/src/android/build.gradle index 286ac86..4773a9c 100644 --- a/src/android/build.gradle +++ b/src/android/build.gradle @@ -1,12 +1,13 @@ buildscript { - ext.kotlin_version = '1.9.21' + ext.gradle_plugin_version = '7.4.0' + ext.kotlin_version = '1.7.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath "com.android.tools.build:gradle:$gradle_plugin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -28,42 +29,4 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir -} - -//run the following once the rust src changes: -//flutter_rust_bridge_codegen --rust-input ./rust/src/api.rs --dart-output ./lib/models/webp_bridge_generated.dart -//cd ./rust && cargo ndk --platform 24 -t armeabi-v7a -t arm64-v8a -o ../android/app/src/main/jniLibs build --release -//TODO: move this to a makefile - -//flutter_rust_bridge docs suggest the bellow. Does not work for me and results in the apk compilation -//looping new gradle tasks and never finishing -// [ -// Debug: null, -// Profile: '--release', -// Release: '--release' -// ].each { -// def taskPostfix = it.key -// def profileMode = it.value -// tasks.whenTaskAdded { task -> -// if (task.name == "javaPreCompile$taskPostfix") { -// task.dependsOn "cargoBuild$taskPostfix" -// } -// } -// tasks.register("cargoBuild$taskPostfix", Exec) { -// workingDir "../../rust" -// environment ANDROID_NDK_HOME: "$ANDROID_NDK" -// commandLine 'cargo', 'ndk', -// '--platform', '24', -// // the 2 ABIs below are used by real Android devices -// '-t', 'armeabi-v7a', -// '-t', 'arm64-v8a', -// // the below 2 ABIs are usually used for Android simulators, -// // add or remove these ABIs as needed. -// // '-t', 'x86', -// // '-t', 'x86_64', -// '-o', '../android/app/src/main/jniLibs', 'build' -// if (profileMode != null) { -// args profileMode -// } -// } -// } +} \ No newline at end of file diff --git a/src/android/gradle/wrapper/gradle-wrapper.properties b/src/android/gradle/wrapper/gradle-wrapper.properties index ae04661..b1624c4 100644 --- a/src/android/gradle/wrapper/gradle-wrapper.properties +++ b/src/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/flutter_rust_bridge.yaml b/src/flutter_rust_bridge.yaml new file mode 100644 index 0000000..f009f9c --- /dev/null +++ b/src/flutter_rust_bridge.yaml @@ -0,0 +1,2 @@ +rust_input: rust/src/api/**/*.rs +dart_output: lib/src/rust \ No newline at end of file diff --git a/src/integration_test/simple_test.dart b/src/integration_test/simple_test.dart new file mode 100644 index 0000000..582b6a7 --- /dev/null +++ b/src/integration_test/simple_test.dart @@ -0,0 +1,14 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:seventv_for_whatsapp/main.dart'; +import 'package:seventv_for_whatsapp/src/rust/frb_generated.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + if (kIsWeb) IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + setUpAll(() async => await RustLib.init()); + testWidgets('Can call rust function', (WidgetTester tester) async { + await tester.pumpWidget(const MyApp()); + expect(find.textContaining('Result: `Hello, Tom!`'), findsOneWidget); + }); +} diff --git a/src/lib/main.dart b/src/lib/main.dart index fc9b9c1..84bc8aa 100644 --- a/src/lib/main.dart +++ b/src/lib/main.dart @@ -119,4 +119,4 @@ class MyApp extends StatelessWidget { home: const Browser(), ); } -} +} \ No newline at end of file diff --git a/src/lib/models/ffi.dart b/src/lib/models/ffi.dart deleted file mode 100644 index 5f69680..0000000 --- a/src/lib/models/ffi.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Re-export the bridge so it is only necessary to import this file. -export 'webp_bridge_generated.dart'; -import 'dart:io' as io; -import 'dart:ffi'; - -import 'package:seventv_for_whatsapp/models/webp_bridge_generated.dart'; - -const _base = 'rust'; - -// On MacOS, the dynamic library is not bundled with the binary, -// but rather directly **linked** against the binary. -final _dylib = io.Platform.isWindows ? '$_base.dll' : 'lib$_base.so'; - -final api = - RustImpl(io.Platform.isIOS || io.Platform.isMacOS ? DynamicLibrary.executable() : DynamicLibrary.open(_dylib)); diff --git a/src/lib/models/settings.dart b/src/lib/models/settings.dart index 1710ee0..543eb76 100644 --- a/src/lib/models/settings.dart +++ b/src/lib/models/settings.dart @@ -9,18 +9,19 @@ class Settings { Settings(); Settings.fromJson(Map json) - : defaultPublisher = json['defaultPublisher']; + : defaultPublisher = json['defaultPublisher']; Map toJson() => { - 'defaultPublisher': defaultPublisher, - }; + 'defaultPublisher': defaultPublisher, + }; } class SettingsManager { static Future load() async { final sharedPreferences = await SharedPreferences.getInstance(); - final jsonString = sharedPreferences.getString(SharedPreferencesKeys.settings); - if(jsonString == null) { + final jsonString = + sharedPreferences.getString(SharedPreferencesKeys.settings); + if (jsonString == null) { return Settings(); } return Settings.fromJson(jsonDecode(jsonString)); @@ -33,4 +34,4 @@ class SettingsManager { jsonEncode(settings.toJson()), ); } -} \ No newline at end of file +} diff --git a/src/lib/models/seventv.dart b/src/lib/models/seventv.dart index fa64c6c..6b30a79 100644 --- a/src/lib/models/seventv.dart +++ b/src/lib/models/seventv.dart @@ -4,8 +4,7 @@ import 'dart:convert'; import 'dart:async'; class SevenTv { - static const _searchEmotesQuery = - r''' + static const _searchEmotesQuery = r''' query SearchEmotes($query: String!, $page: Int, $sort: Sort, $limit: Int, $filter: EmoteSearchFilter) { emotes(query: $query, page: $page, sort: $sort, limit: $limit, filter: $filter) { count @@ -140,7 +139,10 @@ class EmoteTransformer implements StreamTransformer { } else if (c == '{' || c == '[') { _currentDepth++; if (c == '[') { - if (_buffer.replaceAll(' ', '').toLowerCase().endsWith('"items":[')) { + if (_buffer + .replaceAll(' ', '') + .toLowerCase() + .endsWith('"items":[')) { _itemsArrayDepth = _currentDepth; //clear buffer because it now collects emote json _buffer = ''; @@ -150,7 +152,8 @@ class EmoteTransformer implements StreamTransformer { if (_currentDepth == _itemsArrayDepth + 1 && c == '}') { //emote object closed countCollected++; - _controller.add(Emote.fromJson(jsonDecode(_buffer.substring(_buffer.indexOf('{'))))); + _controller.add(Emote.fromJson( + jsonDecode(_buffer.substring(_buffer.indexOf('{'))))); _buffer = ''; } if (_currentDepth == _itemsArrayDepth && _itemsArrayDepth > 0) { @@ -205,7 +208,9 @@ class Emote { Uri? getMaxSizeUrl({Format format = Format.webp}) { final files = host?.files?.where((f) => f.format == format); - final file = files?.isEmpty ?? true ? null : files?.reduce((a, b) => a.height > b.height ? a : b); + final file = files?.isEmpty ?? true + ? null + : files?.reduce((a, b) => a.height > b.height ? a : b); return file == null ? null : host?.getUrl(file); } @@ -296,7 +301,8 @@ class Host { throw "url does not lead to a valid webp"; } // Check if the file is animated by looking for the "ANIM" chunk identifier - final isAnimated = listEquals(bytes.sublist(30, 30 + 4), utf8.encode('ANIM')); + final isAnimated = + listEquals(bytes.sublist(30, 30 + 4), utf8.encode('ANIM')); return isAnimated; } } diff --git a/src/lib/models/webp_bridge_generated.dart b/src/lib/models/webp_bridge_generated.dart deleted file mode 100644 index c7e6fa5..0000000 --- a/src/lib/models/webp_bridge_generated.dart +++ /dev/null @@ -1,870 +0,0 @@ -// AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.82.5. -// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const - -import 'dart:convert'; -import 'dart:async'; -import 'package:meta/meta.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; -import 'package:uuid/uuid.dart'; -import 'package:freezed_annotation/freezed_annotation.dart' hide protected; - -import 'dart:ffi' as ffi; - -part 'webp_bridge_generated.freezed.dart'; - -abstract class Rust { - Future> intoFrames({required Uint8List bytes, dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kIntoFramesConstMeta; - - Future> upscaleFramesWithPadding( - {required List frames, - required int width, - required int height, - dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kUpscaleFramesWithPaddingConstMeta; - - Future encode( - {required List frames, - required EncodingConfig config, - dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kEncodeConstMeta; - - Future calcTranslucency({required List frames, dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kCalcTranslucencyConstMeta; -} - -enum AlphaFilter { - Fast, - Best, -} - -class EncodingConfig { - final int method; - final bool losless; - final double quality; - final int targetSize; - final double targetPsnr; - final int segments; - final int noiseShaping; - final Filter? filter; - final bool alphaCompression; - final AlphaFilter? alphaFiltering; - final int alphaQuality; - final int pass; - final bool showCompressed; - final Preprocessing? preprocessing; - final int partitions; - final int partitionLimit; - final bool useSharpYuv; - - const EncodingConfig({ - required this.method, - required this.losless, - required this.quality, - required this.targetSize, - required this.targetPsnr, - required this.segments, - required this.noiseShaping, - this.filter, - required this.alphaCompression, - this.alphaFiltering, - required this.alphaQuality, - required this.pass, - required this.showCompressed, - this.preprocessing, - required this.partitions, - required this.partitionLimit, - required this.useSharpYuv, - }); -} - -@freezed -sealed class Filter with _$Filter { - const factory Filter.simple( - FilterConfig field0, - ) = Filter_Simple; - const factory Filter.strong( - FilterConfig field0, - ) = Filter_Strong; -} - -class FilterConfig { - final int? strength; - final int sharpness; - - const FilterConfig({ - this.strength, - required this.sharpness, - }); -} - -class Frame { - final Uint8List data; - final int width; - final int height; - final int timestamp; - - const Frame({ - required this.data, - required this.width, - required this.height, - required this.timestamp, - }); -} - -enum Preprocessing { - SegmentSmooth, -} - -class RustImpl implements Rust { - final RustPlatform _platform; - factory RustImpl(ExternalLibrary dylib) => RustImpl.raw(RustPlatform(dylib)); - - /// Only valid on web/WASM platforms. - factory RustImpl.wasm(FutureOr module) => - RustImpl(module as ExternalLibrary); - RustImpl.raw(this._platform); - Future> intoFrames({required Uint8List bytes, dynamic hint}) { - var arg0 = _platform.api2wire_uint_8_list(bytes); - return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_into_frames(port_, arg0), - parseSuccessData: _wire2api_list_frame, - parseErrorData: null, - constMeta: kIntoFramesConstMeta, - argValues: [bytes], - hint: hint, - )); - } - - FlutterRustBridgeTaskConstMeta get kIntoFramesConstMeta => - const FlutterRustBridgeTaskConstMeta( - debugName: "into_frames", - argNames: ["bytes"], - ); - - Future> upscaleFramesWithPadding( - {required List frames, - required int width, - required int height, - dynamic hint}) { - var arg0 = _platform.api2wire_list_frame(frames); - var arg1 = api2wire_u32(width); - var arg2 = api2wire_u32(height); - return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner - .wire_upscale_frames_with_padding(port_, arg0, arg1, arg2), - parseSuccessData: _wire2api_list_frame, - parseErrorData: null, - constMeta: kUpscaleFramesWithPaddingConstMeta, - argValues: [frames, width, height], - hint: hint, - )); - } - - FlutterRustBridgeTaskConstMeta get kUpscaleFramesWithPaddingConstMeta => - const FlutterRustBridgeTaskConstMeta( - debugName: "upscale_frames_with_padding", - argNames: ["frames", "width", "height"], - ); - - Future encode( - {required List frames, - required EncodingConfig config, - dynamic hint}) { - var arg0 = _platform.api2wire_list_frame(frames); - var arg1 = _platform.api2wire_box_autoadd_encoding_config(config); - return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_encode(port_, arg0, arg1), - parseSuccessData: _wire2api_uint_8_list, - parseErrorData: null, - constMeta: kEncodeConstMeta, - argValues: [frames, config], - hint: hint, - )); - } - - FlutterRustBridgeTaskConstMeta get kEncodeConstMeta => - const FlutterRustBridgeTaskConstMeta( - debugName: "encode", - argNames: ["frames", "config"], - ); - - Future calcTranslucency({required List frames, dynamic hint}) { - var arg0 = _platform.api2wire_list_frame(frames); - return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_calc_translucency(port_, arg0), - parseSuccessData: _wire2api_f32, - parseErrorData: null, - constMeta: kCalcTranslucencyConstMeta, - argValues: [frames], - hint: hint, - )); - } - - FlutterRustBridgeTaskConstMeta get kCalcTranslucencyConstMeta => - const FlutterRustBridgeTaskConstMeta( - debugName: "calc_translucency", - argNames: ["frames"], - ); - - void dispose() { - _platform.dispose(); - } -// Section: wire2api - - double _wire2api_f32(dynamic raw) { - return raw as double; - } - - Frame _wire2api_frame(dynamic raw) { - final arr = raw as List; - if (arr.length != 4) - throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); - return Frame( - data: _wire2api_uint_8_list(arr[0]), - width: _wire2api_u32(arr[1]), - height: _wire2api_u32(arr[2]), - timestamp: _wire2api_i32(arr[3]), - ); - } - - int _wire2api_i32(dynamic raw) { - return raw as int; - } - - List _wire2api_list_frame(dynamic raw) { - return (raw as List).map(_wire2api_frame).toList(); - } - - int _wire2api_u32(dynamic raw) { - return raw as int; - } - - int _wire2api_u8(dynamic raw) { - return raw as int; - } - - Uint8List _wire2api_uint_8_list(dynamic raw) { - return raw as Uint8List; - } -} - -// Section: api2wire - -@protected -int api2wire_alpha_filter(AlphaFilter raw) { - return api2wire_i32(raw.index); -} - -@protected -bool api2wire_bool(bool raw) { - return raw; -} - -@protected -double api2wire_f32(double raw) { - return raw; -} - -@protected -int api2wire_i32(int raw) { - return raw; -} - -@protected -int api2wire_preprocessing(Preprocessing raw) { - return api2wire_i32(raw.index); -} - -@protected -int api2wire_u32(int raw) { - return raw; -} - -@protected -int api2wire_u8(int raw) { - return raw; -} - -// Section: finalizer - -class RustPlatform extends FlutterRustBridgeBase { - RustPlatform(ffi.DynamicLibrary dylib) : super(RustWire(dylib)); - -// Section: api2wire - - @protected - ffi.Pointer api2wire_box_autoadd_alpha_filter(AlphaFilter raw) { - return inner.new_box_autoadd_alpha_filter_0(api2wire_alpha_filter(raw)); - } - - @protected - ffi.Pointer api2wire_box_autoadd_encoding_config( - EncodingConfig raw) { - final ptr = inner.new_box_autoadd_encoding_config_0(); - _api_fill_to_wire_encoding_config(raw, ptr.ref); - return ptr; - } - - @protected - ffi.Pointer api2wire_box_autoadd_filter(Filter raw) { - final ptr = inner.new_box_autoadd_filter_0(); - _api_fill_to_wire_filter(raw, ptr.ref); - return ptr; - } - - @protected - ffi.Pointer api2wire_box_autoadd_filter_config( - FilterConfig raw) { - final ptr = inner.new_box_autoadd_filter_config_0(); - _api_fill_to_wire_filter_config(raw, ptr.ref); - return ptr; - } - - @protected - ffi.Pointer api2wire_box_autoadd_preprocessing(Preprocessing raw) { - return inner.new_box_autoadd_preprocessing_0(api2wire_preprocessing(raw)); - } - - @protected - ffi.Pointer api2wire_box_autoadd_u8(int raw) { - return inner.new_box_autoadd_u8_0(api2wire_u8(raw)); - } - - @protected - ffi.Pointer api2wire_list_frame(List raw) { - final ans = inner.new_list_frame_0(raw.length); - for (var i = 0; i < raw.length; ++i) { - _api_fill_to_wire_frame(raw[i], ans.ref.ptr[i]); - } - return ans; - } - - @protected - ffi.Pointer api2wire_opt_box_autoadd_alpha_filter( - AlphaFilter? raw) { - return raw == null ? ffi.nullptr : api2wire_box_autoadd_alpha_filter(raw); - } - - @protected - ffi.Pointer api2wire_opt_box_autoadd_filter(Filter? raw) { - return raw == null ? ffi.nullptr : api2wire_box_autoadd_filter(raw); - } - - @protected - ffi.Pointer api2wire_opt_box_autoadd_preprocessing( - Preprocessing? raw) { - return raw == null ? ffi.nullptr : api2wire_box_autoadd_preprocessing(raw); - } - - @protected - ffi.Pointer api2wire_opt_box_autoadd_u8(int? raw) { - return raw == null ? ffi.nullptr : api2wire_box_autoadd_u8(raw); - } - - @protected - ffi.Pointer 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: api_fill_to_wire - - void _api_fill_to_wire_box_autoadd_encoding_config( - EncodingConfig apiObj, ffi.Pointer wireObj) { - _api_fill_to_wire_encoding_config(apiObj, wireObj.ref); - } - - void _api_fill_to_wire_box_autoadd_filter( - Filter apiObj, ffi.Pointer wireObj) { - _api_fill_to_wire_filter(apiObj, wireObj.ref); - } - - void _api_fill_to_wire_box_autoadd_filter_config( - FilterConfig apiObj, ffi.Pointer wireObj) { - _api_fill_to_wire_filter_config(apiObj, wireObj.ref); - } - - void _api_fill_to_wire_encoding_config( - EncodingConfig apiObj, wire_EncodingConfig wireObj) { - wireObj.method = api2wire_u8(apiObj.method); - wireObj.losless = api2wire_bool(apiObj.losless); - wireObj.quality = api2wire_f32(apiObj.quality); - wireObj.target_size = api2wire_i32(apiObj.targetSize); - wireObj.target_psnr = api2wire_f32(apiObj.targetPsnr); - wireObj.segments = api2wire_u8(apiObj.segments); - wireObj.noise_shaping = api2wire_u8(apiObj.noiseShaping); - wireObj.filter = api2wire_opt_box_autoadd_filter(apiObj.filter); - wireObj.alpha_compression = api2wire_bool(apiObj.alphaCompression); - wireObj.alpha_filtering = - api2wire_opt_box_autoadd_alpha_filter(apiObj.alphaFiltering); - wireObj.alpha_quality = api2wire_u8(apiObj.alphaQuality); - wireObj.pass = api2wire_u8(apiObj.pass); - wireObj.show_compressed = api2wire_bool(apiObj.showCompressed); - wireObj.preprocessing = - api2wire_opt_box_autoadd_preprocessing(apiObj.preprocessing); - wireObj.partitions = api2wire_u8(apiObj.partitions); - wireObj.partition_limit = api2wire_u8(apiObj.partitionLimit); - wireObj.use_sharp_yuv = api2wire_bool(apiObj.useSharpYuv); - } - - void _api_fill_to_wire_filter(Filter apiObj, wire_Filter wireObj) { - if (apiObj is Filter_Simple) { - var pre_field0 = api2wire_box_autoadd_filter_config(apiObj.field0); - wireObj.tag = 0; - wireObj.kind = inner.inflate_Filter_Simple(); - wireObj.kind.ref.Simple.ref.field0 = pre_field0; - return; - } - if (apiObj is Filter_Strong) { - var pre_field0 = api2wire_box_autoadd_filter_config(apiObj.field0); - wireObj.tag = 1; - wireObj.kind = inner.inflate_Filter_Strong(); - wireObj.kind.ref.Strong.ref.field0 = pre_field0; - return; - } - } - - void _api_fill_to_wire_filter_config( - FilterConfig apiObj, wire_FilterConfig wireObj) { - wireObj.strength = api2wire_opt_box_autoadd_u8(apiObj.strength); - wireObj.sharpness = api2wire_u8(apiObj.sharpness); - } - - void _api_fill_to_wire_frame(Frame apiObj, wire_Frame wireObj) { - wireObj.data = api2wire_uint_8_list(apiObj.data); - wireObj.width = api2wire_u32(apiObj.width); - wireObj.height = api2wire_u32(apiObj.height); - wireObj.timestamp = api2wire_i32(apiObj.timestamp); - } -} - -// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names - -// AUTO GENERATED FILE, DO NOT EDIT. -// -// Generated by `package:ffigen`. -// ignore_for_file: type=lint - -/// generated by flutter_rust_bridge -class RustWire implements FlutterRustBridgeWireBase { - @internal - late final dartApi = DartApiDl(init_frb_dart_api_dl); - - /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; - - /// The symbols are looked up in [dynamicLibrary]. - RustWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - - /// The symbols are looked up with [lookup]. - RustWire.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; - - void store_dart_post_cobject( - DartPostCObjectFnType ptr, - ) { - return _store_dart_post_cobject( - ptr, - ); - } - - late final _store_dart_post_cobjectPtr = - _lookup>( - 'store_dart_post_cobject'); - late final _store_dart_post_cobject = _store_dart_post_cobjectPtr - .asFunction(); - - Object get_dart_object( - int ptr, - ) { - return _get_dart_object( - ptr, - ); - } - - late final _get_dart_objectPtr = - _lookup>( - 'get_dart_object'); - late final _get_dart_object = - _get_dart_objectPtr.asFunction(); - - void drop_dart_object( - int ptr, - ) { - return _drop_dart_object( - ptr, - ); - } - - late final _drop_dart_objectPtr = - _lookup>( - 'drop_dart_object'); - late final _drop_dart_object = - _drop_dart_objectPtr.asFunction(); - - int new_dart_opaque( - Object handle, - ) { - return _new_dart_opaque( - handle, - ); - } - - late final _new_dart_opaquePtr = - _lookup>( - 'new_dart_opaque'); - late final _new_dart_opaque = - _new_dart_opaquePtr.asFunction(); - - int init_frb_dart_api_dl( - ffi.Pointer obj, - ) { - return _init_frb_dart_api_dl( - obj, - ); - } - - late final _init_frb_dart_api_dlPtr = - _lookup)>>( - 'init_frb_dart_api_dl'); - late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr - .asFunction)>(); - - void wire_into_frames( - int port_, - ffi.Pointer bytes, - ) { - return _wire_into_frames( - port_, - bytes, - ); - } - - late final _wire_into_framesPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, ffi.Pointer)>>('wire_into_frames'); - late final _wire_into_frames = _wire_into_framesPtr - .asFunction)>(); - - void wire_upscale_frames_with_padding( - int port_, - ffi.Pointer frames, - int width, - int height, - ) { - return _wire_upscale_frames_with_padding( - port_, - frames, - width, - height, - ); - } - - late final _wire_upscale_frames_with_paddingPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Int64, ffi.Pointer, ffi.Uint32, - ffi.Uint32)>>('wire_upscale_frames_with_padding'); - late final _wire_upscale_frames_with_padding = - _wire_upscale_frames_with_paddingPtr.asFunction< - void Function(int, ffi.Pointer, int, int)>(); - - void wire_encode( - int port_, - ffi.Pointer frames, - ffi.Pointer config, - ) { - return _wire_encode( - port_, - frames, - config, - ); - } - - late final _wire_encodePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Int64, ffi.Pointer, - ffi.Pointer)>>('wire_encode'); - late final _wire_encode = _wire_encodePtr.asFunction< - void Function(int, ffi.Pointer, - ffi.Pointer)>(); - - void wire_calc_translucency( - int port_, - ffi.Pointer frames, - ) { - return _wire_calc_translucency( - port_, - frames, - ); - } - - late final _wire_calc_translucencyPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Int64, - ffi.Pointer)>>('wire_calc_translucency'); - late final _wire_calc_translucency = _wire_calc_translucencyPtr - .asFunction)>(); - - ffi.Pointer new_box_autoadd_alpha_filter_0( - int value, - ) { - return _new_box_autoadd_alpha_filter_0( - value, - ); - } - - late final _new_box_autoadd_alpha_filter_0Ptr = - _lookup Function(ffi.Int32)>>( - 'new_box_autoadd_alpha_filter_0'); - late final _new_box_autoadd_alpha_filter_0 = - _new_box_autoadd_alpha_filter_0Ptr - .asFunction Function(int)>(); - - ffi.Pointer new_box_autoadd_encoding_config_0() { - return _new_box_autoadd_encoding_config_0(); - } - - late final _new_box_autoadd_encoding_config_0Ptr = - _lookup Function()>>( - 'new_box_autoadd_encoding_config_0'); - late final _new_box_autoadd_encoding_config_0 = - _new_box_autoadd_encoding_config_0Ptr - .asFunction Function()>(); - - ffi.Pointer new_box_autoadd_filter_0() { - return _new_box_autoadd_filter_0(); - } - - late final _new_box_autoadd_filter_0Ptr = - _lookup Function()>>( - 'new_box_autoadd_filter_0'); - late final _new_box_autoadd_filter_0 = _new_box_autoadd_filter_0Ptr - .asFunction Function()>(); - - ffi.Pointer new_box_autoadd_filter_config_0() { - return _new_box_autoadd_filter_config_0(); - } - - late final _new_box_autoadd_filter_config_0Ptr = - _lookup Function()>>( - 'new_box_autoadd_filter_config_0'); - late final _new_box_autoadd_filter_config_0 = - _new_box_autoadd_filter_config_0Ptr - .asFunction Function()>(); - - ffi.Pointer new_box_autoadd_preprocessing_0( - int value, - ) { - return _new_box_autoadd_preprocessing_0( - value, - ); - } - - late final _new_box_autoadd_preprocessing_0Ptr = - _lookup Function(ffi.Int32)>>( - 'new_box_autoadd_preprocessing_0'); - late final _new_box_autoadd_preprocessing_0 = - _new_box_autoadd_preprocessing_0Ptr - .asFunction Function(int)>(); - - ffi.Pointer new_box_autoadd_u8_0( - int value, - ) { - return _new_box_autoadd_u8_0( - value, - ); - } - - late final _new_box_autoadd_u8_0Ptr = - _lookup Function(ffi.Uint8)>>( - 'new_box_autoadd_u8_0'); - late final _new_box_autoadd_u8_0 = _new_box_autoadd_u8_0Ptr - .asFunction Function(int)>(); - - ffi.Pointer new_list_frame_0( - int len, - ) { - return _new_list_frame_0( - len, - ); - } - - late final _new_list_frame_0Ptr = _lookup< - ffi.NativeFunction Function(ffi.Int32)>>( - 'new_list_frame_0'); - late final _new_list_frame_0 = _new_list_frame_0Ptr - .asFunction Function(int)>(); - - ffi.Pointer new_uint_8_list_0( - int len, - ) { - return _new_uint_8_list_0( - len, - ); - } - - late final _new_uint_8_list_0Ptr = _lookup< - ffi - .NativeFunction Function(ffi.Int32)>>( - 'new_uint_8_list_0'); - late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr - .asFunction Function(int)>(); - - ffi.Pointer inflate_Filter_Simple() { - return _inflate_Filter_Simple(); - } - - late final _inflate_Filter_SimplePtr = - _lookup Function()>>( - 'inflate_Filter_Simple'); - late final _inflate_Filter_Simple = _inflate_Filter_SimplePtr - .asFunction Function()>(); - - ffi.Pointer inflate_Filter_Strong() { - return _inflate_Filter_Strong(); - } - - late final _inflate_Filter_StrongPtr = - _lookup Function()>>( - 'inflate_Filter_Strong'); - late final _inflate_Filter_Strong = _inflate_Filter_StrongPtr - .asFunction Function()>(); - - void free_WireSyncReturn( - WireSyncReturn ptr, - ) { - return _free_WireSyncReturn( - ptr, - ); - } - - late final _free_WireSyncReturnPtr = - _lookup>( - 'free_WireSyncReturn'); - late final _free_WireSyncReturn = - _free_WireSyncReturnPtr.asFunction(); -} - -final class _Dart_Handle extends ffi.Opaque {} - -final class wire_uint_8_list extends ffi.Struct { - external ffi.Pointer ptr; - - @ffi.Int32() - external int len; -} - -final class wire_Frame extends ffi.Struct { - external ffi.Pointer data; - - @ffi.Uint32() - external int width; - - @ffi.Uint32() - external int height; - - @ffi.Int32() - external int timestamp; -} - -final class wire_list_frame extends ffi.Struct { - external ffi.Pointer ptr; - - @ffi.Int32() - external int len; -} - -final class wire_FilterConfig extends ffi.Struct { - external ffi.Pointer strength; - - @ffi.Uint8() - external int sharpness; -} - -final class wire_Filter_Simple extends ffi.Struct { - external ffi.Pointer field0; -} - -final class wire_Filter_Strong extends ffi.Struct { - external ffi.Pointer field0; -} - -final class FilterKind extends ffi.Union { - external ffi.Pointer Simple; - - external ffi.Pointer Strong; -} - -final class wire_Filter extends ffi.Struct { - @ffi.Int32() - external int tag; - - external ffi.Pointer kind; -} - -final class wire_EncodingConfig extends ffi.Struct { - @ffi.Uint8() - external int method; - - @ffi.Bool() - external bool losless; - - @ffi.Float() - external double quality; - - @ffi.Int32() - external int target_size; - - @ffi.Float() - external double target_psnr; - - @ffi.Uint8() - external int segments; - - @ffi.Uint8() - external int noise_shaping; - - external ffi.Pointer filter; - - @ffi.Bool() - external bool alpha_compression; - - external ffi.Pointer alpha_filtering; - - @ffi.Uint8() - external int alpha_quality; - - @ffi.Uint8() - external int pass; - - @ffi.Bool() - external bool show_compressed; - - external ffi.Pointer preprocessing; - - @ffi.Uint8() - external int partitions; - - @ffi.Uint8() - external int partition_limit; - - @ffi.Bool() - external bool use_sharp_yuv; -} - -typedef DartPostCObjectFnType = ffi.Pointer< - ffi.NativeFunction< - ffi.Bool Function(DartPort port_id, ffi.Pointer message)>>; -typedef DartPort = ffi.Int64; diff --git a/src/lib/models/whatsapp.dart b/src/lib/models/whatsapp.dart index 3304584..99d6aa9 100644 --- a/src/lib/models/whatsapp.dart +++ b/src/lib/models/whatsapp.dart @@ -6,8 +6,10 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; // import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:seventv_for_whatsapp/models/ffi.dart'; +import 'package:seventv_for_whatsapp/src/rust/api/api.dart'; import 'package:seventv_for_whatsapp/models/shared_preferences_keys.dart'; +import 'package:seventv_for_whatsapp/src/rust/webp/encode.dart'; +import 'package:seventv_for_whatsapp/src/rust/webp/shared.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:ulid/ulid.dart'; import 'package:universal_io/io.dart' as io; @@ -18,15 +20,18 @@ import 'seventv.dart'; class WhatsApp { static Future getStickerDirectory() async { - final applicationDocumentsDirectory = await getApplicationDocumentsDirectory(); - final packDirectory = io.Directory('${applicationDocumentsDirectory.path}/stickers'); + final applicationDocumentsDirectory = + await getApplicationDocumentsDirectory(); + final packDirectory = + io.Directory('${applicationDocumentsDirectory.path}/stickers'); return packDirectory.create(recursive: true); } static Future> loadStoredStickerPacks() async { final sharedPreferences = await SharedPreferences.getInstance(); final stickerPackIds = - sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? []; + sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? + []; return stickerPackIds .map((id) => sharedPreferences.getString(id)) .where((json) => json != null) @@ -77,7 +82,8 @@ class StickerPack { licenseAgreementWebsite?.toString(), isAnimated, { for (var sticker in stickers) - WhatsappStickerImageHandler.fromFile(sticker.imagePath).path: sticker.emojis + WhatsappStickerImageHandler.fromFile(sticker.imagePath).path: + sticker.emojis }); isInstalled = true; await save(); @@ -100,16 +106,19 @@ class StickerPack { Future save() async { final id = identifier.toString(); final sharedPreferences = await SharedPreferences.getInstance(); - final stickerPacks = sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? []; + final stickerPacks = + sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? + []; final existingPack = sharedPreferences.getString(id); if (!stickerPacks.contains(id) && existingPack == null) { stickerPacks.add(identifier.toString()); debugPrint('new stickerpack $id added to shared preferences'); } else { // merge stickers with existing (might happen if two stickers are added at the same time) - final existingStickers = StickerPack.fromJson(jsonDecode(existingPack!)).stickers; - stickers.addAll(existingStickers.where( - (existing) => !stickers.any((current) => current.identifier == existing.identifier))); + final existingStickers = + StickerPack.fromJson(jsonDecode(existingPack!)).stickers; + stickers.addAll(existingStickers.where((existing) => !stickers + .any((current) => current.identifier == existing.identifier))); } if (!await sharedPreferences.setString(id, jsonEncode(this))) { @@ -117,7 +126,8 @@ class StickerPack { } debugPrint('saved stickerpack $id'); - return await sharedPreferences.setStringList(SharedPreferencesKeys.stickerPacks, stickerPacks); + return await sharedPreferences.setStringList( + SharedPreferencesKeys.stickerPacks, stickerPacks); } Future delete() async { @@ -126,7 +136,9 @@ class StickerPack { final id = identifier.toString(); final sharedPreferences = await SharedPreferences.getInstance(); - final stickerPacks = sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? []; + final stickerPacks = + sharedPreferences.getStringList(SharedPreferencesKeys.stickerPacks) ?? + []; if (stickerPacks.remove(identifier.toString())) { debugPrint('stickerpack $id removed from shared preferences'); } @@ -136,7 +148,8 @@ class StickerPack { } debugPrint('deleted stickerpack $id'); - return await sharedPreferences.setStringList(SharedPreferencesKeys.stickerPacks, stickerPacks); + return await sharedPreferences.setStringList( + SharedPreferencesKeys.stickerPacks, stickerPacks); } StickerPack.fromJson(Map json) @@ -151,7 +164,8 @@ class StickerPack { licenseAgreementWebsite = json['licenseAgreementWebsite'], isAnimated = json['isAnimated'], isInstalled = json['isInstalled'], - stickers = List.from(json['stickers'].map((sticker) => Sticker.fromJson(sticker))); + stickers = List.from( + json['stickers'].map((sticker) => Sticker.fromJson(sticker))); Map toJson() => { 'identifier': identifier.toString(), @@ -190,7 +204,8 @@ class Sticker { } final request = await httpClient.getUrl(url); final response = await request.close(); - final imagePath = '${(await WhatsApp.getStickerDirectory()).path}/$identifier.webp'; + final imagePath = + '${(await WhatsApp.getStickerDirectory()).path}/$identifier.webp'; var bytes = Uint8List.fromList(await response.expand((b) => b).toList()); final webp = WebPDecoder(bytes); @@ -201,7 +216,7 @@ class Sticker { final webpInformation = webp.info!; bool isAnimated = webpInformation.hasAnimation; - var frames = await api.intoFrames(bytes: Uint8List.fromList(bytes)); + var frames = await intoFrames(bytes: Uint8List.fromList(bytes)); if (frames.isEmpty) { throw Exception('WebP does not seem to have a single frame'); @@ -214,7 +229,8 @@ class Sticker { if (duration >= maxStickerDuration) { // TODO: We could also either speed the animation up or remove frames from the beginning. // Eventually it would be cool to make this configurable - debugPrint('Emote animation duration exceeds the maximum allowed animation duration for ' + debugPrint( + 'Emote animation duration exceeds the maximum allowed animation duration for ' 'stickers. Cutting some frames of the end.'); frames.removeWhere((frame) => frame.timestamp >= maxStickerDuration); @@ -224,7 +240,8 @@ class Sticker { //resize webp to 512x512 bytes = await _resizeFrames(frames, bytes.length, isAnimated); - debugPrint('generated sticker webp is ${(bytes.length / 1024).toStringAsFixed(2)}KB large'); + debugPrint( + 'generated sticker webp is ${(bytes.length / 1024).toStringAsFixed(2)}KB large'); await io.File(imagePath).writeAsBytes(bytes); @@ -235,7 +252,8 @@ class Sticker { return List.empty(); } - static Future _resizeFrames(List frames, int size, bool isAnimated) async { + static Future _resizeFrames( + List frames, int size, bool isAnimated) async { final losless = size / 1024 <= 100; const minSize = 1; final maxSize = isAnimated ? maxStickerSizeAnimated : maxStickerSizeStatic; @@ -260,7 +278,8 @@ class Sticker { //sometimes (rarely in my experience) the webp encoder goes slightly over the set targetSize, //this is why we decrease this value for every failed attempt targetSize = max(minSize.toDouble(), generatedSize - difference * 1.6); - debugPrint('Set target webp size to ${(targetSize / 1024).toStringAsFixed(2)}KB'); + debugPrint( + 'Set target webp size to ${(targetSize / 1024).toStringAsFixed(2)}KB'); final encodingConfig = EncodingConfig( losless: losless, quality: 100, @@ -275,11 +294,12 @@ class Sticker { partitions: 0, partitionLimit: 0, useSharpYuv: false, - alphaFiltering: AlphaFilter.Fast, + alphaFiltering: AlphaFilter.fast, filter: const Filter.strong(FilterConfig(strength: 60, sharpness: 0)), method: 6); - frames = await api.upscaleFramesWithPadding(frames: frames, width: 512, height: 512); - upscaled = await api.encode(frames: frames, config: encodingConfig); + frames = await upscaleFramesWithPadding( + frames: frames, width: 512, height: 512); + upscaled = await encode(frames: frames, config: encodingConfig); generatedSize = upscaled.length; attempt++; } diff --git a/src/lib/screens/browser.dart b/src/lib/screens/browser.dart index 5161576..1ab0ecb 100644 --- a/src/lib/screens/browser.dart +++ b/src/lib/screens/browser.dart @@ -47,7 +47,8 @@ class _BrowserState extends State { _searchController.addListener(() => setState(() {})); _notificationService.initialize(); _scrollController.addListener(() async { - if (_scrollController.offset == _scrollController.position.maxScrollExtent) { + if (_scrollController.offset == + _scrollController.position.maxScrollExtent) { await getEmotes(); } }); @@ -126,35 +127,45 @@ class _BrowserState extends State { showDialog( context: context, builder: (dialogContext) { - return AlertDialog(content: Image.network(emote.getMaxSizeUrl().toString()), actions: [ - TextButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close')), - OutlinedButton( - child: const Text('Add'), - onPressed: () { - Navigator.pop(dialogContext); - Navigator.of(dialogContext).push(PageRouteBuilder( - opaque: false, - pageBuilder: (BuildContext context, _, __) => EmoteEmojiPicker(emote, [ - EmoteEmojiAction(_addToExistingPackLabel, _createStickerAndAddToPack), - EmoteEmojiAction(_createNewPackLabel, (ctx, emote, emojis) async { - var stickerPack = await _emoteToStickerPack(emote, emojis); - if (stickerPack == null) { - return; - } - debugPrint( - 'sticker saved at ${(await WhatsApp.getStickerDirectory()).path}'); - }), - ]))); - }, - ), - ]); + return AlertDialog( + content: Image.network(emote.getMaxSizeUrl().toString()), + actions: [ + TextButton( + onPressed: () => Navigator.pop(dialogContext), + child: const Text('Close')), + OutlinedButton( + child: const Text('Add'), + onPressed: () { + Navigator.pop(dialogContext); + Navigator.of(dialogContext).push(PageRouteBuilder( + opaque: false, + pageBuilder: (BuildContext context, _, __) => + EmoteEmojiPicker(emote, [ + EmoteEmojiAction(_addToExistingPackLabel, + _createStickerAndAddToPack), + EmoteEmojiAction(_createNewPackLabel, + (ctx, emote, emojis) async { + var stickerPack = + await _emoteToStickerPack(emote, emojis); + if (stickerPack == null) { + return; + } + debugPrint( + 'sticker saved at ${(await WhatsApp.getStickerDirectory()).path}'); + }), + ]))); + }, + ), + ]); }); } - Future _createStickerAndAddToPack(BuildContext _, Emote emote, List emojis) async { + Future _createStickerAndAddToPack( + BuildContext _, Emote emote, List emojis) async { //TODO: minor performance improvement if we download the full webp in the background here after returning whether //it is animated or not and then pass it when creating the Sticker - final emoteIsAnimated = await emote.host!.checkIfAnimated(emote.getMaxSizeFile()); + final emoteIsAnimated = + await emote.host!.checkIfAnimated(emote.getMaxSizeFile()); debugPrint('emote is animated: $emoteIsAnimated'); if (!mounted) { return; @@ -167,7 +178,8 @@ class _BrowserState extends State { Navigator.pop(ctx, pack); return null; }, - filter: (pack) => (pack.isAnimated ?? emoteIsAnimated) != emoteIsAnimated, + filter: (pack) => + (pack.isAnimated ?? emoteIsAnimated) != emoteIsAnimated, ))); if (selectedPack != null) { debugPrint('selected stickerpack ${selectedPack.name}'); @@ -175,18 +187,21 @@ class _BrowserState extends State { selectedPack.addSticker(sticker); selectedPack.isAnimated = emoteIsAnimated; selectedPack.save(); - debugPrint('added sticker "${sticker.identifier}" to stickerpack "${selectedPack.name}"'); + debugPrint( + 'added sticker "${sticker.identifier}" to stickerpack "${selectedPack.name}"'); } } - Future _emoteToStickerPack(Emote emote, List emojis) async { + Future _emoteToStickerPack( + Emote emote, List emojis) async { final settings = await SettingsManager.load(); if (context.mounted) { final stickerPack = await showDialog( context: context, builder: (dialogContext) { return CreateStickerPackDialog( - defaultName: emote.name, defaultPublisher: settings.defaultPublisher); + defaultName: emote.name, + defaultPublisher: settings.defaultPublisher); }); if (stickerPack != null) { var sticker = await _emoteToSticker(emote, emojis); @@ -204,8 +219,8 @@ class _BrowserState extends State { try { //TODO: button to go to stickerpack final sticker = await Sticker.fromEmote(emote, emojis); - _messengerKey.currentState - ?.showSnackBar(SnackBar(content: Text('Done processing \'${emote.name}\''))); + _messengerKey.currentState?.showSnackBar( + SnackBar(content: Text('Done processing \'${emote.name}\''))); return sticker; } finally { await _notificationService.endProcessing(emote.id); @@ -216,11 +231,12 @@ class _BrowserState extends State { Navigator.push(context, MaterialPageRoute(builder: (ctx) { return StickerPacks( (pack) async { - var state = await Navigator.push( - context, MaterialPageRoute(builder: (ctx) => views.StickerPack(pack))); + var state = await Navigator.push(context, + MaterialPageRoute(builder: (ctx) => views.StickerPack(pack))); var isDeleted = state?.isDeleted ?? false; debugPrint('isDeleted: $isDeleted'); - return StickerPackSelectedCallbackResult(reloadRequired: state?.isDeleted ?? false); + return StickerPackSelectedCallbackResult( + reloadRequired: state?.isDeleted ?? false); }, ); })); @@ -243,7 +259,9 @@ class _BrowserState extends State { child: Scaffold( appBar: AppBar( //TODO: implement willpopscope to go back to trending from search - title: _isSearchMode ? _createSearchField() : const Text('7TV for WhatsApp'), + title: _isSearchMode + ? _createSearchField() + : const Text('7TV for WhatsApp'), actions: !_isSearchMode ? [ IconButton( @@ -253,16 +271,20 @@ class _BrowserState extends State { : _searchController.text.isNotEmpty ? [ IconButton( - onPressed: () => setState(() => _searchController.text = ''), + onPressed: () => + setState(() => _searchController.text = ''), icon: const Icon(Icons.clear)) ] : null, leading: _isSearchMode - ? IconButton(onPressed: goToTrending, icon: const Icon(Icons.arrow_back)) - : IconButton(onPressed: goToStickerPacks, icon: const Icon(Icons.apps)), + ? IconButton( + onPressed: goToTrending, icon: const Icon(Icons.arrow_back)) + : IconButton( + onPressed: goToStickerPacks, icon: const Icon(Icons.apps)), ), body: _isLoading - ? const Skeleton(gridDelegate: _gridDelegate, searchCunkSize: _chunkSize) + ? const Skeleton( + gridDelegate: _gridDelegate, searchCunkSize: _chunkSize) : _loadedEmotes.isEmpty ? const Center( child: Text( @@ -343,8 +365,8 @@ class Skeleton extends StatelessWidget { margin: const EdgeInsets.all(3), decoration: ShapeDecoration( color: Colors.black, - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)))), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40)))), childCount: _searchCunkSize)) ], physics: const NeverScrollableScrollPhysics(), diff --git a/src/lib/screens/stickerpack.dart b/src/lib/screens/stickerpack.dart index e31d7b0..5e36973 100644 --- a/src/lib/screens/stickerpack.dart +++ b/src/lib/screens/stickerpack.dart @@ -42,11 +42,13 @@ class _StickerPackState extends State { 'Deleting this sticker pack will remove all the stickers associated with it. Do you want to proceed?'), actions: [ TextButton( - onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancel')), + onPressed: () => Navigator.pop(ctx, false), + child: const Text('Cancel')), TextButton( onPressed: () => Navigator.pop(ctx, true), - child: - Text('Delete', style: TextStyle(color: Theme.of(ctx).colorScheme.error))) + child: Text('Delete', + style: TextStyle( + color: Theme.of(ctx).colorScheme.error))) ])); if (!(delete ?? false)) { return false; @@ -64,7 +66,9 @@ class _StickerPackState extends State { return Scaffold( appBar: AppBar( title: Text(widget.stickerPack.name), - actions: [IconButton(onPressed: _delete, icon: const Icon(Icons.delete))], + actions: [ + IconButton(onPressed: _delete, icon: const Icon(Icons.delete)) + ], ), body: CustomScrollView( slivers: [ @@ -82,11 +86,18 @@ class _StickerPackState extends State { ], ), floatingActionButton: FloatingActionButton.extended( - backgroundColor: !isValidPack ? const Color.fromARGB(255, 120, 120, 120) : null, - foregroundColor: !isValidPack ? const Color.fromARGB(255, 70, 70, 70) : null, - onPressed: !isValidPack ? null : () => _addToWhatsApp(widget.stickerPack), + backgroundColor: + !isValidPack ? const Color.fromARGB(255, 120, 120, 120) : null, + foregroundColor: + !isValidPack ? const Color.fromARGB(255, 70, 70, 70) : null, + onPressed: + !isValidPack ? null : () => _addToWhatsApp(widget.stickerPack), label: const Row( - children: [Icon(Icons.add), SizedBox(width: 2), Text('Add to WhatsApp')], + children: [ + Icon(Icons.add), + SizedBox(width: 2), + Text('Add to WhatsApp') + ], )), ); } diff --git a/src/lib/screens/stickerpacks.dart b/src/lib/screens/stickerpacks.dart index ccc86b6..dc63aea 100644 --- a/src/lib/screens/stickerpacks.dart +++ b/src/lib/screens/stickerpacks.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:seventv_for_whatsapp/models/whatsapp.dart'; import 'package:seventv_for_whatsapp/widgets/create_stickerpack_dialog.dart'; - class StickerPackSelectedCallbackResult { final bool reloadRequired; @@ -10,7 +9,8 @@ class StickerPackSelectedCallbackResult { } class StickerPacks extends StatefulWidget { - final Future? Function(StickerPack) onStickerPackSelected; + final Future? Function(StickerPack) + onStickerPackSelected; final bool Function(StickerPack)? filter; const StickerPacks(this.onStickerPackSelected, {super.key, this.filter}); @@ -33,7 +33,8 @@ class StickerPacksState extends State { debugPrint('loading stickerpacks'); for (var pack in await WhatsApp.loadStoredStickerPacks()) { setState(() => _loadedPacks.add(pack)); - debugPrint('loaded stickerpack ${pack.name} - isAnimated: ${pack.isAnimated}'); + debugPrint( + 'loaded stickerpack ${pack.name} - isAnimated: ${pack.isAnimated}'); } } @@ -61,7 +62,8 @@ class StickerPacksState extends State { slivers: [ SliverGrid( gridDelegate: _gridDelegate, - delegate: SliverChildListDelegate([for (final pack in _loadedPacks) _buildPackContainer(pack)]), + delegate: SliverChildListDelegate( + [for (final pack in _loadedPacks) _buildPackContainer(pack)]), ), ], ), @@ -100,7 +102,10 @@ class StickerPacksState extends State { ) : GestureDetector( onTap: () async => - (await widget.onStickerPackSelected(pack))?.reloadRequired == true ? await _loadPacks() : null, + (await widget.onStickerPackSelected(pack))?.reloadRequired == + true + ? await _loadPacks() + : null, child: child); } } diff --git a/src/lib/services/notification_service.dart b/src/lib/services/notification_service.dart index a934a5a..dd2dfef 100644 --- a/src/lib/services/notification_service.dart +++ b/src/lib/services/notification_service.dart @@ -3,10 +3,12 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class NotificationService { static const _processingChannelId = 'processingChannel'; static const _processingChannelName = 'Processing'; - static const _processingChannelDescription = 'Shown when a sticker is being processed'; + static const _processingChannelDescription = + 'Shown when a sticker is being processed'; final _service = FlutterLocalNotificationsPlugin(); Future initialize() => - _service.initialize(const InitializationSettings(android: AndroidInitializationSettings('mipmap/ic_launcher'))); + _service.initialize(const InitializationSettings( + android: AndroidInitializationSettings('mipmap/ic_launcher'))); Future startProcessing(String emoteId, String stickerName) async { await _service.show( @@ -14,7 +16,8 @@ class NotificationService { 'Processing sticker \'$stickerName\'...', null, const NotificationDetails( - android: AndroidNotificationDetails(_processingChannelId, _processingChannelName, + android: AndroidNotificationDetails( + _processingChannelId, _processingChannelName, channelDescription: _processingChannelDescription, // importance: Importance.max, // priority: Priority.max, @@ -23,7 +26,8 @@ class NotificationService { ongoing: true))); } - Future endProcessing(String emoteId) => _service.cancel(emoteId.hashCode); + Future endProcessing(String emoteId) => + _service.cancel(emoteId.hashCode); Future cancel(int id) => _service.cancel(id); } diff --git a/src/lib/src/rust/api/api.dart b/src/lib/src/rust/api/api.dart new file mode 100644 index 0000000..0923085 --- /dev/null +++ b/src/lib/src/rust/api/api.dart @@ -0,0 +1,29 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import '../webp/encode.dart'; +import '../webp/shared.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +Future> intoFrames({required Uint8List bytes, dynamic hint}) => + RustLib.instance.api.intoFrames(bytes: bytes, hint: hint); + +Future> upscaleFramesWithPadding( + {required List frames, + required int width, + required int height, + dynamic hint}) => + RustLib.instance.api.upscaleFramesWithPadding( + frames: frames, width: width, height: height, hint: hint); + +Future encode( + {required List frames, + required EncodingConfig config, + dynamic hint}) => + RustLib.instance.api.encode(frames: frames, config: config, hint: hint); + +Future calcTranslucency({required List frames, dynamic hint}) => + RustLib.instance.api.calcTranslucency(frames: frames, hint: hint); diff --git a/src/lib/src/rust/frb_generated.dart b/src/lib/src/rust/frb_generated.dart new file mode 100644 index 0000000..15073cb --- /dev/null +++ b/src/lib/src/rust/frb_generated.dart @@ -0,0 +1,768 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables + +import 'api/api.dart'; +import 'dart:async'; +import 'dart:convert'; +import 'frb_generated.io.dart' if (dart.library.html) 'frb_generated.web.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'webp/encode.dart'; +import 'webp/shared.dart'; + +/// Main entrypoint of the Rust API +class RustLib extends BaseEntrypoint { + @internal + static final instance = RustLib._(); + + RustLib._(); + + /// Initialize flutter_rust_bridge + static Future init({ + RustLibApi? api, + BaseHandler? handler, + ExternalLibrary? externalLibrary, + }) async { + await instance.initImpl( + api: api, + handler: handler, + externalLibrary: externalLibrary, + ); + } + + /// Dispose flutter_rust_bridge + /// + /// The call to this function is optional, since flutter_rust_bridge (and everything else) + /// is automatically disposed when the app stops. + static void dispose() => instance.disposeImpl(); + + @override + ApiImplConstructor get apiImplConstructor => + RustLibApiImpl.new; + + @override + WireConstructor get wireConstructor => + RustLibWire.fromExternalLibrary; + + @override + Future executeRustInitializers() async {} + + @override + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => + kDefaultExternalLibraryLoaderConfig; + + static const kDefaultExternalLibraryLoaderConfig = + ExternalLibraryLoaderConfig( + stem: 'rust', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + ); +} + +abstract class RustLibApi extends BaseApi { + Future calcTranslucency({required List frames, dynamic hint}); + + Future encode( + {required List frames, + required EncodingConfig config, + dynamic hint}); + + Future> intoFrames({required Uint8List bytes, dynamic hint}); + + Future> upscaleFramesWithPadding( + {required List frames, + required int width, + required int height, + dynamic hint}); +} + +class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { + RustLibApiImpl({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @override + Future calcTranslucency({required List frames, dynamic hint}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_list_frame(frames); + return wire.wire_calc_translucency(port_, arg0); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_f_32, + decodeErrorData: null, + ), + constMeta: kCalcTranslucencyConstMeta, + argValues: [frames], + apiImpl: this, + hint: hint, + )); + } + + TaskConstMeta get kCalcTranslucencyConstMeta => const TaskConstMeta( + debugName: "calc_translucency", + argNames: ["frames"], + ); + + @override + Future encode( + {required List frames, + required EncodingConfig config, + dynamic hint}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_list_frame(frames); + var arg1 = cst_encode_box_autoadd_encoding_config(config); + return wire.wire_encode(port_, arg0, arg1); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_list_prim_u_8, + decodeErrorData: null, + ), + constMeta: kEncodeConstMeta, + argValues: [frames, config], + apiImpl: this, + hint: hint, + )); + } + + TaskConstMeta get kEncodeConstMeta => const TaskConstMeta( + debugName: "encode", + argNames: ["frames", "config"], + ); + + @override + Future> intoFrames({required Uint8List bytes, dynamic hint}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_list_prim_u_8(bytes); + return wire.wire_into_frames(port_, arg0); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_list_frame, + decodeErrorData: null, + ), + constMeta: kIntoFramesConstMeta, + argValues: [bytes], + apiImpl: this, + hint: hint, + )); + } + + TaskConstMeta get kIntoFramesConstMeta => const TaskConstMeta( + debugName: "into_frames", + argNames: ["bytes"], + ); + + @override + Future> upscaleFramesWithPadding( + {required List frames, + required int width, + required int height, + dynamic hint}) { + return handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_list_frame(frames); + var arg1 = cst_encode_u_32(width); + var arg2 = cst_encode_u_32(height); + return wire.wire_upscale_frames_with_padding(port_, arg0, arg1, arg2); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_list_frame, + decodeErrorData: null, + ), + constMeta: kUpscaleFramesWithPaddingConstMeta, + argValues: [frames, width, height], + apiImpl: this, + hint: hint, + )); + } + + TaskConstMeta get kUpscaleFramesWithPaddingConstMeta => const TaskConstMeta( + debugName: "upscale_frames_with_padding", + argNames: ["frames", "width", "height"], + ); + + @protected + AlphaFilter dco_decode_alpha_filter(dynamic raw) { + return AlphaFilter.values[raw as int]; + } + + @protected + bool dco_decode_bool(dynamic raw) { + return raw as bool; + } + + @protected + AlphaFilter dco_decode_box_autoadd_alpha_filter(dynamic raw) { + return dco_decode_alpha_filter(raw); + } + + @protected + EncodingConfig dco_decode_box_autoadd_encoding_config(dynamic raw) { + return dco_decode_encoding_config(raw); + } + + @protected + Filter dco_decode_box_autoadd_filter(dynamic raw) { + return dco_decode_filter(raw); + } + + @protected + FilterConfig dco_decode_box_autoadd_filter_config(dynamic raw) { + return dco_decode_filter_config(raw); + } + + @protected + Preprocessing dco_decode_box_autoadd_preprocessing(dynamic raw) { + return dco_decode_preprocessing(raw); + } + + @protected + int dco_decode_box_autoadd_u_8(dynamic raw) { + return raw as int; + } + + @protected + EncodingConfig dco_decode_encoding_config(dynamic raw) { + final arr = raw as List; + if (arr.length != 17) + throw Exception('unexpected arr length: expect 17 but see ${arr.length}'); + return EncodingConfig( + method: dco_decode_u_8(arr[0]), + losless: dco_decode_bool(arr[1]), + quality: dco_decode_f_32(arr[2]), + targetSize: dco_decode_i_32(arr[3]), + targetPsnr: dco_decode_f_32(arr[4]), + segments: dco_decode_u_8(arr[5]), + noiseShaping: dco_decode_u_8(arr[6]), + filter: dco_decode_opt_box_autoadd_filter(arr[7]), + alphaCompression: dco_decode_bool(arr[8]), + alphaFiltering: dco_decode_opt_box_autoadd_alpha_filter(arr[9]), + alphaQuality: dco_decode_u_8(arr[10]), + pass: dco_decode_u_8(arr[11]), + showCompressed: dco_decode_bool(arr[12]), + preprocessing: dco_decode_opt_box_autoadd_preprocessing(arr[13]), + partitions: dco_decode_u_8(arr[14]), + partitionLimit: dco_decode_u_8(arr[15]), + useSharpYuv: dco_decode_bool(arr[16]), + ); + } + + @protected + double dco_decode_f_32(dynamic raw) { + return raw as double; + } + + @protected + Filter dco_decode_filter(dynamic raw) { + switch (raw[0]) { + case 0: + return Filter_Simple( + dco_decode_box_autoadd_filter_config(raw[1]), + ); + case 1: + return Filter_Strong( + dco_decode_box_autoadd_filter_config(raw[1]), + ); + default: + throw Exception("unreachable"); + } + } + + @protected + FilterConfig dco_decode_filter_config(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return FilterConfig( + strength: dco_decode_opt_box_autoadd_u_8(arr[0]), + sharpness: dco_decode_u_8(arr[1]), + ); + } + + @protected + Frame dco_decode_frame(dynamic raw) { + final arr = raw as List; + if (arr.length != 4) + throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + return Frame( + data: dco_decode_list_prim_u_8(arr[0]), + width: dco_decode_u_32(arr[1]), + height: dco_decode_u_32(arr[2]), + timestamp: dco_decode_i_32(arr[3]), + ); + } + + @protected + int dco_decode_i_32(dynamic raw) { + return raw as int; + } + + @protected + List dco_decode_list_frame(dynamic raw) { + return (raw as List).map(dco_decode_frame).toList(); + } + + @protected + Uint8List dco_decode_list_prim_u_8(dynamic raw) { + return raw as Uint8List; + } + + @protected + AlphaFilter? dco_decode_opt_box_autoadd_alpha_filter(dynamic raw) { + return raw == null ? null : dco_decode_box_autoadd_alpha_filter(raw); + } + + @protected + Filter? dco_decode_opt_box_autoadd_filter(dynamic raw) { + return raw == null ? null : dco_decode_box_autoadd_filter(raw); + } + + @protected + Preprocessing? dco_decode_opt_box_autoadd_preprocessing(dynamic raw) { + return raw == null ? null : dco_decode_box_autoadd_preprocessing(raw); + } + + @protected + int? dco_decode_opt_box_autoadd_u_8(dynamic raw) { + return raw == null ? null : dco_decode_box_autoadd_u_8(raw); + } + + @protected + Preprocessing dco_decode_preprocessing(dynamic raw) { + return Preprocessing.values[raw as int]; + } + + @protected + int dco_decode_u_32(dynamic raw) { + return raw as int; + } + + @protected + int dco_decode_u_8(dynamic raw) { + return raw as int; + } + + @protected + void dco_decode_unit(dynamic raw) { + return; + } + + @protected + AlphaFilter sse_decode_alpha_filter(SseDeserializer deserializer) { + var inner = sse_decode_i_32(deserializer); + return AlphaFilter.values[inner]; + } + + @protected + bool sse_decode_bool(SseDeserializer deserializer) { + return deserializer.buffer.getUint8() != 0; + } + + @protected + AlphaFilter sse_decode_box_autoadd_alpha_filter( + SseDeserializer deserializer) { + return (sse_decode_alpha_filter(deserializer)); + } + + @protected + EncodingConfig sse_decode_box_autoadd_encoding_config( + SseDeserializer deserializer) { + return (sse_decode_encoding_config(deserializer)); + } + + @protected + Filter sse_decode_box_autoadd_filter(SseDeserializer deserializer) { + return (sse_decode_filter(deserializer)); + } + + @protected + FilterConfig sse_decode_box_autoadd_filter_config( + SseDeserializer deserializer) { + return (sse_decode_filter_config(deserializer)); + } + + @protected + Preprocessing sse_decode_box_autoadd_preprocessing( + SseDeserializer deserializer) { + return (sse_decode_preprocessing(deserializer)); + } + + @protected + int sse_decode_box_autoadd_u_8(SseDeserializer deserializer) { + return (sse_decode_u_8(deserializer)); + } + + @protected + EncodingConfig sse_decode_encoding_config(SseDeserializer deserializer) { + var var_method = sse_decode_u_8(deserializer); + var var_losless = sse_decode_bool(deserializer); + var var_quality = sse_decode_f_32(deserializer); + var var_targetSize = sse_decode_i_32(deserializer); + var var_targetPsnr = sse_decode_f_32(deserializer); + var var_segments = sse_decode_u_8(deserializer); + var var_noiseShaping = sse_decode_u_8(deserializer); + var var_filter = sse_decode_opt_box_autoadd_filter(deserializer); + var var_alphaCompression = sse_decode_bool(deserializer); + var var_alphaFiltering = + sse_decode_opt_box_autoadd_alpha_filter(deserializer); + var var_alphaQuality = sse_decode_u_8(deserializer); + var var_pass = sse_decode_u_8(deserializer); + var var_showCompressed = sse_decode_bool(deserializer); + var var_preprocessing = + sse_decode_opt_box_autoadd_preprocessing(deserializer); + var var_partitions = sse_decode_u_8(deserializer); + var var_partitionLimit = sse_decode_u_8(deserializer); + var var_useSharpYuv = sse_decode_bool(deserializer); + return EncodingConfig( + method: var_method, + losless: var_losless, + quality: var_quality, + targetSize: var_targetSize, + targetPsnr: var_targetPsnr, + segments: var_segments, + noiseShaping: var_noiseShaping, + filter: var_filter, + alphaCompression: var_alphaCompression, + alphaFiltering: var_alphaFiltering, + alphaQuality: var_alphaQuality, + pass: var_pass, + showCompressed: var_showCompressed, + preprocessing: var_preprocessing, + partitions: var_partitions, + partitionLimit: var_partitionLimit, + useSharpYuv: var_useSharpYuv); + } + + @protected + double sse_decode_f_32(SseDeserializer deserializer) { + return deserializer.buffer.getFloat32(); + } + + @protected + Filter sse_decode_filter(SseDeserializer deserializer) { + var tag_ = sse_decode_i_32(deserializer); + switch (tag_) { + case 0: + var var_field0 = sse_decode_box_autoadd_filter_config(deserializer); + return Filter_Simple(var_field0); + case 1: + var var_field0 = sse_decode_box_autoadd_filter_config(deserializer); + return Filter_Strong(var_field0); + default: + throw UnimplementedError(''); + } + } + + @protected + FilterConfig sse_decode_filter_config(SseDeserializer deserializer) { + var var_strength = sse_decode_opt_box_autoadd_u_8(deserializer); + var var_sharpness = sse_decode_u_8(deserializer); + return FilterConfig(strength: var_strength, sharpness: var_sharpness); + } + + @protected + Frame sse_decode_frame(SseDeserializer deserializer) { + var var_data = sse_decode_list_prim_u_8(deserializer); + var var_width = sse_decode_u_32(deserializer); + var var_height = sse_decode_u_32(deserializer); + var var_timestamp = sse_decode_i_32(deserializer); + return Frame( + data: var_data, + width: var_width, + height: var_height, + timestamp: var_timestamp); + } + + @protected + int sse_decode_i_32(SseDeserializer deserializer) { + return deserializer.buffer.getInt32(); + } + + @protected + List sse_decode_list_frame(SseDeserializer deserializer) { + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_frame(deserializer)); + } + return ans_; + } + + @protected + Uint8List sse_decode_list_prim_u_8(SseDeserializer deserializer) { + var len_ = sse_decode_i_32(deserializer); + return deserializer.buffer.getUint8List(len_); + } + + @protected + AlphaFilter? sse_decode_opt_box_autoadd_alpha_filter( + SseDeserializer deserializer) { + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_alpha_filter(deserializer)); + } else { + return null; + } + } + + @protected + Filter? sse_decode_opt_box_autoadd_filter(SseDeserializer deserializer) { + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_filter(deserializer)); + } else { + return null; + } + } + + @protected + Preprocessing? sse_decode_opt_box_autoadd_preprocessing( + SseDeserializer deserializer) { + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_preprocessing(deserializer)); + } else { + return null; + } + } + + @protected + int? sse_decode_opt_box_autoadd_u_8(SseDeserializer deserializer) { + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_u_8(deserializer)); + } else { + return null; + } + } + + @protected + Preprocessing sse_decode_preprocessing(SseDeserializer deserializer) { + var inner = sse_decode_i_32(deserializer); + return Preprocessing.values[inner]; + } + + @protected + int sse_decode_u_32(SseDeserializer deserializer) { + return deserializer.buffer.getUint32(); + } + + @protected + int sse_decode_u_8(SseDeserializer deserializer) { + return deserializer.buffer.getUint8(); + } + + @protected + void sse_decode_unit(SseDeserializer deserializer) {} + + @protected + int cst_encode_alpha_filter(AlphaFilter raw) { + return cst_encode_i_32(raw.index); + } + + @protected + bool cst_encode_bool(bool raw) { + return raw; + } + + @protected + double cst_encode_f_32(double raw) { + return raw; + } + + @protected + int cst_encode_i_32(int raw) { + return raw; + } + + @protected + int cst_encode_preprocessing(Preprocessing raw) { + return cst_encode_i_32(raw.index); + } + + @protected + int cst_encode_u_32(int raw) { + return raw; + } + + @protected + int cst_encode_u_8(int raw) { + return raw; + } + + @protected + void cst_encode_unit(void raw) { + return raw; + } + + @protected + void sse_encode_alpha_filter(AlphaFilter self, SseSerializer serializer) { + sse_encode_i_32(self.index, serializer); + } + + @protected + void sse_encode_bool(bool self, SseSerializer serializer) { + serializer.buffer.putUint8(self ? 1 : 0); + } + + @protected + void sse_encode_box_autoadd_alpha_filter( + AlphaFilter self, SseSerializer serializer) { + sse_encode_alpha_filter(self, serializer); + } + + @protected + void sse_encode_box_autoadd_encoding_config( + EncodingConfig self, SseSerializer serializer) { + sse_encode_encoding_config(self, serializer); + } + + @protected + void sse_encode_box_autoadd_filter(Filter self, SseSerializer serializer) { + sse_encode_filter(self, serializer); + } + + @protected + void sse_encode_box_autoadd_filter_config( + FilterConfig self, SseSerializer serializer) { + sse_encode_filter_config(self, serializer); + } + + @protected + void sse_encode_box_autoadd_preprocessing( + Preprocessing self, SseSerializer serializer) { + sse_encode_preprocessing(self, serializer); + } + + @protected + void sse_encode_box_autoadd_u_8(int self, SseSerializer serializer) { + sse_encode_u_8(self, serializer); + } + + @protected + void sse_encode_encoding_config( + EncodingConfig self, SseSerializer serializer) { + sse_encode_u_8(self.method, serializer); + sse_encode_bool(self.losless, serializer); + sse_encode_f_32(self.quality, serializer); + sse_encode_i_32(self.targetSize, serializer); + sse_encode_f_32(self.targetPsnr, serializer); + sse_encode_u_8(self.segments, serializer); + sse_encode_u_8(self.noiseShaping, serializer); + sse_encode_opt_box_autoadd_filter(self.filter, serializer); + sse_encode_bool(self.alphaCompression, serializer); + sse_encode_opt_box_autoadd_alpha_filter(self.alphaFiltering, serializer); + sse_encode_u_8(self.alphaQuality, serializer); + sse_encode_u_8(self.pass, serializer); + sse_encode_bool(self.showCompressed, serializer); + sse_encode_opt_box_autoadd_preprocessing(self.preprocessing, serializer); + sse_encode_u_8(self.partitions, serializer); + sse_encode_u_8(self.partitionLimit, serializer); + sse_encode_bool(self.useSharpYuv, serializer); + } + + @protected + void sse_encode_f_32(double self, SseSerializer serializer) { + serializer.buffer.putFloat32(self); + } + + @protected + void sse_encode_filter(Filter self, SseSerializer serializer) { + switch (self) { + case Filter_Simple(field0: final field0): + sse_encode_i_32(0, serializer); + sse_encode_box_autoadd_filter_config(field0, serializer); + case Filter_Strong(field0: final field0): + sse_encode_i_32(1, serializer); + sse_encode_box_autoadd_filter_config(field0, serializer); + } + } + + @protected + void sse_encode_filter_config(FilterConfig self, SseSerializer serializer) { + sse_encode_opt_box_autoadd_u_8(self.strength, serializer); + sse_encode_u_8(self.sharpness, serializer); + } + + @protected + void sse_encode_frame(Frame self, SseSerializer serializer) { + sse_encode_list_prim_u_8(self.data, serializer); + sse_encode_u_32(self.width, serializer); + sse_encode_u_32(self.height, serializer); + sse_encode_i_32(self.timestamp, serializer); + } + + @protected + void sse_encode_i_32(int self, SseSerializer serializer) { + serializer.buffer.putInt32(self); + } + + @protected + void sse_encode_list_frame(List self, SseSerializer serializer) { + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_frame(item, serializer); + } + } + + @protected + void sse_encode_list_prim_u_8(Uint8List self, SseSerializer serializer) { + sse_encode_i_32(self.length, serializer); + serializer.buffer.putUint8List(self); + } + + @protected + void sse_encode_opt_box_autoadd_alpha_filter( + AlphaFilter? self, SseSerializer serializer) { + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_alpha_filter(self, serializer); + } + } + + @protected + void sse_encode_opt_box_autoadd_filter( + Filter? self, SseSerializer serializer) { + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_filter(self, serializer); + } + } + + @protected + void sse_encode_opt_box_autoadd_preprocessing( + Preprocessing? self, SseSerializer serializer) { + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_preprocessing(self, serializer); + } + } + + @protected + void sse_encode_opt_box_autoadd_u_8(int? self, SseSerializer serializer) { + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_u_8(self, serializer); + } + } + + @protected + void sse_encode_preprocessing(Preprocessing self, SseSerializer serializer) { + sse_encode_i_32(self.index, serializer); + } + + @protected + void sse_encode_u_32(int self, SseSerializer serializer) { + serializer.buffer.putUint32(self); + } + + @protected + void sse_encode_u_8(int self, SseSerializer serializer) { + serializer.buffer.putUint8(self); + } + + @protected + void sse_encode_unit(void self, SseSerializer serializer) {} +} diff --git a/src/lib/src/rust/frb_generated.io.dart b/src/lib/src/rust/frb_generated.io.dart new file mode 100644 index 0000000..19a66cd --- /dev/null +++ b/src/lib/src/rust/frb_generated.io.dart @@ -0,0 +1,778 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables + +import 'api/api.dart'; +import 'dart:async'; +import 'dart:convert'; +import 'dart:ffi' as ffi; +import 'frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; +import 'webp/encode.dart'; +import 'webp/shared.dart'; + +abstract class RustLibApiImplPlatform extends BaseApiImpl { + RustLibApiImplPlatform({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @protected + AlphaFilter dco_decode_alpha_filter(dynamic raw); + + @protected + bool dco_decode_bool(dynamic raw); + + @protected + AlphaFilter dco_decode_box_autoadd_alpha_filter(dynamic raw); + + @protected + EncodingConfig dco_decode_box_autoadd_encoding_config(dynamic raw); + + @protected + Filter dco_decode_box_autoadd_filter(dynamic raw); + + @protected + FilterConfig dco_decode_box_autoadd_filter_config(dynamic raw); + + @protected + Preprocessing dco_decode_box_autoadd_preprocessing(dynamic raw); + + @protected + int dco_decode_box_autoadd_u_8(dynamic raw); + + @protected + EncodingConfig dco_decode_encoding_config(dynamic raw); + + @protected + double dco_decode_f_32(dynamic raw); + + @protected + Filter dco_decode_filter(dynamic raw); + + @protected + FilterConfig dco_decode_filter_config(dynamic raw); + + @protected + Frame dco_decode_frame(dynamic raw); + + @protected + int dco_decode_i_32(dynamic raw); + + @protected + List dco_decode_list_frame(dynamic raw); + + @protected + Uint8List dco_decode_list_prim_u_8(dynamic raw); + + @protected + AlphaFilter? dco_decode_opt_box_autoadd_alpha_filter(dynamic raw); + + @protected + Filter? dco_decode_opt_box_autoadd_filter(dynamic raw); + + @protected + Preprocessing? dco_decode_opt_box_autoadd_preprocessing(dynamic raw); + + @protected + int? dco_decode_opt_box_autoadd_u_8(dynamic raw); + + @protected + Preprocessing dco_decode_preprocessing(dynamic raw); + + @protected + int dco_decode_u_32(dynamic raw); + + @protected + int dco_decode_u_8(dynamic raw); + + @protected + void dco_decode_unit(dynamic raw); + + @protected + AlphaFilter sse_decode_alpha_filter(SseDeserializer deserializer); + + @protected + bool sse_decode_bool(SseDeserializer deserializer); + + @protected + AlphaFilter sse_decode_box_autoadd_alpha_filter(SseDeserializer deserializer); + + @protected + EncodingConfig sse_decode_box_autoadd_encoding_config( + SseDeserializer deserializer); + + @protected + Filter sse_decode_box_autoadd_filter(SseDeserializer deserializer); + + @protected + FilterConfig sse_decode_box_autoadd_filter_config( + SseDeserializer deserializer); + + @protected + Preprocessing sse_decode_box_autoadd_preprocessing( + SseDeserializer deserializer); + + @protected + int sse_decode_box_autoadd_u_8(SseDeserializer deserializer); + + @protected + EncodingConfig sse_decode_encoding_config(SseDeserializer deserializer); + + @protected + double sse_decode_f_32(SseDeserializer deserializer); + + @protected + Filter sse_decode_filter(SseDeserializer deserializer); + + @protected + FilterConfig sse_decode_filter_config(SseDeserializer deserializer); + + @protected + Frame sse_decode_frame(SseDeserializer deserializer); + + @protected + int sse_decode_i_32(SseDeserializer deserializer); + + @protected + List sse_decode_list_frame(SseDeserializer deserializer); + + @protected + Uint8List sse_decode_list_prim_u_8(SseDeserializer deserializer); + + @protected + AlphaFilter? sse_decode_opt_box_autoadd_alpha_filter( + SseDeserializer deserializer); + + @protected + Filter? sse_decode_opt_box_autoadd_filter(SseDeserializer deserializer); + + @protected + Preprocessing? sse_decode_opt_box_autoadd_preprocessing( + SseDeserializer deserializer); + + @protected + int? sse_decode_opt_box_autoadd_u_8(SseDeserializer deserializer); + + @protected + Preprocessing sse_decode_preprocessing(SseDeserializer deserializer); + + @protected + int sse_decode_u_32(SseDeserializer deserializer); + + @protected + int sse_decode_u_8(SseDeserializer deserializer); + + @protected + void sse_decode_unit(SseDeserializer deserializer); + + @protected + ffi.Pointer cst_encode_box_autoadd_alpha_filter(AlphaFilter raw) { + return wire.cst_new_box_autoadd_alpha_filter(cst_encode_alpha_filter(raw)); + } + + @protected + ffi.Pointer cst_encode_box_autoadd_encoding_config( + EncodingConfig raw) { + final ptr = wire.cst_new_box_autoadd_encoding_config(); + cst_api_fill_to_wire_encoding_config(raw, ptr.ref); + return ptr; + } + + @protected + ffi.Pointer cst_encode_box_autoadd_filter(Filter raw) { + final ptr = wire.cst_new_box_autoadd_filter(); + cst_api_fill_to_wire_filter(raw, ptr.ref); + return ptr; + } + + @protected + ffi.Pointer cst_encode_box_autoadd_filter_config( + FilterConfig raw) { + final ptr = wire.cst_new_box_autoadd_filter_config(); + cst_api_fill_to_wire_filter_config(raw, ptr.ref); + return ptr; + } + + @protected + ffi.Pointer cst_encode_box_autoadd_preprocessing( + Preprocessing raw) { + return wire + .cst_new_box_autoadd_preprocessing(cst_encode_preprocessing(raw)); + } + + @protected + ffi.Pointer cst_encode_box_autoadd_u_8(int raw) { + return wire.cst_new_box_autoadd_u_8(cst_encode_u_8(raw)); + } + + @protected + ffi.Pointer cst_encode_list_frame(List raw) { + final ans = wire.cst_new_list_frame(raw.length); + for (var i = 0; i < raw.length; ++i) { + cst_api_fill_to_wire_frame(raw[i], ans.ref.ptr[i]); + } + return ans; + } + + @protected + ffi.Pointer cst_encode_list_prim_u_8(Uint8List raw) { + final ans = wire.cst_new_list_prim_u_8(raw.length); + ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); + return ans; + } + + @protected + ffi.Pointer cst_encode_opt_box_autoadd_alpha_filter( + AlphaFilter? raw) { + return raw == null ? ffi.nullptr : cst_encode_box_autoadd_alpha_filter(raw); + } + + @protected + ffi.Pointer cst_encode_opt_box_autoadd_filter(Filter? raw) { + return raw == null ? ffi.nullptr : cst_encode_box_autoadd_filter(raw); + } + + @protected + ffi.Pointer cst_encode_opt_box_autoadd_preprocessing( + Preprocessing? raw) { + return raw == null + ? ffi.nullptr + : cst_encode_box_autoadd_preprocessing(raw); + } + + @protected + ffi.Pointer cst_encode_opt_box_autoadd_u_8(int? raw) { + return raw == null ? ffi.nullptr : cst_encode_box_autoadd_u_8(raw); + } + + @protected + void cst_api_fill_to_wire_box_autoadd_encoding_config( + EncodingConfig apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_encoding_config(apiObj, wireObj.ref); + } + + @protected + void cst_api_fill_to_wire_box_autoadd_filter( + Filter apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_filter(apiObj, wireObj.ref); + } + + @protected + void cst_api_fill_to_wire_box_autoadd_filter_config( + FilterConfig apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_filter_config(apiObj, wireObj.ref); + } + + @protected + void cst_api_fill_to_wire_encoding_config( + EncodingConfig apiObj, wire_cst_encoding_config wireObj) { + wireObj.method = cst_encode_u_8(apiObj.method); + wireObj.losless = cst_encode_bool(apiObj.losless); + wireObj.quality = cst_encode_f_32(apiObj.quality); + wireObj.target_size = cst_encode_i_32(apiObj.targetSize); + wireObj.target_psnr = cst_encode_f_32(apiObj.targetPsnr); + wireObj.segments = cst_encode_u_8(apiObj.segments); + wireObj.noise_shaping = cst_encode_u_8(apiObj.noiseShaping); + wireObj.filter = cst_encode_opt_box_autoadd_filter(apiObj.filter); + wireObj.alpha_compression = cst_encode_bool(apiObj.alphaCompression); + wireObj.alpha_filtering = + cst_encode_opt_box_autoadd_alpha_filter(apiObj.alphaFiltering); + wireObj.alpha_quality = cst_encode_u_8(apiObj.alphaQuality); + wireObj.pass = cst_encode_u_8(apiObj.pass); + wireObj.show_compressed = cst_encode_bool(apiObj.showCompressed); + wireObj.preprocessing = + cst_encode_opt_box_autoadd_preprocessing(apiObj.preprocessing); + wireObj.partitions = cst_encode_u_8(apiObj.partitions); + wireObj.partition_limit = cst_encode_u_8(apiObj.partitionLimit); + wireObj.use_sharp_yuv = cst_encode_bool(apiObj.useSharpYuv); + } + + @protected + void cst_api_fill_to_wire_filter(Filter apiObj, wire_cst_filter wireObj) { + if (apiObj is Filter_Simple) { + var pre_field0 = cst_encode_box_autoadd_filter_config(apiObj.field0); + wireObj.tag = 0; + wireObj.kind.Simple.field0 = pre_field0; + return; + } + if (apiObj is Filter_Strong) { + var pre_field0 = cst_encode_box_autoadd_filter_config(apiObj.field0); + wireObj.tag = 1; + wireObj.kind.Strong.field0 = pre_field0; + return; + } + } + + @protected + void cst_api_fill_to_wire_filter_config( + FilterConfig apiObj, wire_cst_filter_config wireObj) { + wireObj.strength = cst_encode_opt_box_autoadd_u_8(apiObj.strength); + wireObj.sharpness = cst_encode_u_8(apiObj.sharpness); + } + + @protected + void cst_api_fill_to_wire_frame(Frame apiObj, wire_cst_frame wireObj) { + wireObj.data = cst_encode_list_prim_u_8(apiObj.data); + wireObj.width = cst_encode_u_32(apiObj.width); + wireObj.height = cst_encode_u_32(apiObj.height); + wireObj.timestamp = cst_encode_i_32(apiObj.timestamp); + } + + @protected + int cst_encode_alpha_filter(AlphaFilter raw); + + @protected + bool cst_encode_bool(bool raw); + + @protected + double cst_encode_f_32(double raw); + + @protected + int cst_encode_i_32(int raw); + + @protected + int cst_encode_preprocessing(Preprocessing raw); + + @protected + int cst_encode_u_32(int raw); + + @protected + int cst_encode_u_8(int raw); + + @protected + void cst_encode_unit(void raw); + + @protected + void sse_encode_alpha_filter(AlphaFilter self, SseSerializer serializer); + + @protected + void sse_encode_bool(bool self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_alpha_filter( + AlphaFilter self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_encoding_config( + EncodingConfig self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_filter(Filter self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_filter_config( + FilterConfig self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_preprocessing( + Preprocessing self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_encoding_config( + EncodingConfig self, SseSerializer serializer); + + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + + @protected + void sse_encode_filter(Filter self, SseSerializer serializer); + + @protected + void sse_encode_filter_config(FilterConfig self, SseSerializer serializer); + + @protected + void sse_encode_frame(Frame self, SseSerializer serializer); + + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + + @protected + void sse_encode_list_frame(List self, SseSerializer serializer); + + @protected + void sse_encode_list_prim_u_8(Uint8List self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_alpha_filter( + AlphaFilter? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_filter( + Filter? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_preprocessing( + Preprocessing? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_u_8(int? self, SseSerializer serializer); + + @protected + void sse_encode_preprocessing(Preprocessing self, SseSerializer serializer); + + @protected + void sse_encode_u_32(int self, SseSerializer serializer); + + @protected + void sse_encode_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_unit(void self, SseSerializer serializer); +} + +// Section: wire_class + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +/// generated by flutter_rust_bridge +class RustLibWire implements BaseWire { + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => + RustLibWire(lib.ffiDynamicLibrary); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + RustLibWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + RustLibWire.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + void dart_fn_deliver_output( + int call_id, + ffi.Pointer ptr_, + int rust_vec_len_, + int data_len_, + ) { + return _dart_fn_deliver_output( + call_id, + ptr_, + rust_vec_len_, + data_len_, + ); + } + + late final _dart_fn_deliver_outputPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int32, ffi.Pointer, ffi.Int32, ffi.Int32)>>( + 'frbgen_seventv_for_whatsapp_dart_fn_deliver_output'); + late final _dart_fn_deliver_output = _dart_fn_deliver_outputPtr + .asFunction, int, int)>(); + + void wire_calc_translucency( + int port_, + ffi.Pointer frames, + ) { + return _wire_calc_translucency( + port_, + frames, + ); + } + + late final _wire_calc_translucencyPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, ffi.Pointer)>>( + 'frbgen_seventv_for_whatsapp_wire_calc_translucency'); + late final _wire_calc_translucency = _wire_calc_translucencyPtr + .asFunction)>(); + + void wire_encode( + int port_, + ffi.Pointer frames, + ffi.Pointer config, + ) { + return _wire_encode( + port_, + frames, + config, + ); + } + + late final _wire_encodePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, ffi.Pointer, + ffi.Pointer)>>( + 'frbgen_seventv_for_whatsapp_wire_encode'); + late final _wire_encode = _wire_encodePtr.asFunction< + void Function(int, ffi.Pointer, + ffi.Pointer)>(); + + void wire_into_frames( + int port_, + ffi.Pointer bytes, + ) { + return _wire_into_frames( + port_, + bytes, + ); + } + + late final _wire_into_framesPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, ffi.Pointer)>>( + 'frbgen_seventv_for_whatsapp_wire_into_frames'); + late final _wire_into_frames = _wire_into_framesPtr + .asFunction)>(); + + void wire_upscale_frames_with_padding( + int port_, + ffi.Pointer frames, + int width, + int height, + ) { + return _wire_upscale_frames_with_padding( + port_, + frames, + width, + height, + ); + } + + late final _wire_upscale_frames_with_paddingPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, ffi.Pointer, + ffi.Uint32, ffi.Uint32)>>( + 'frbgen_seventv_for_whatsapp_wire_upscale_frames_with_padding'); + late final _wire_upscale_frames_with_padding = + _wire_upscale_frames_with_paddingPtr.asFunction< + void Function(int, ffi.Pointer, int, int)>(); + + ffi.Pointer cst_new_box_autoadd_alpha_filter( + int value, + ) { + return _cst_new_box_autoadd_alpha_filter( + value, + ); + } + + late final _cst_new_box_autoadd_alpha_filterPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_alpha_filter'); + late final _cst_new_box_autoadd_alpha_filter = + _cst_new_box_autoadd_alpha_filterPtr + .asFunction Function(int)>(); + + ffi.Pointer cst_new_box_autoadd_encoding_config() { + return _cst_new_box_autoadd_encoding_config(); + } + + late final _cst_new_box_autoadd_encoding_configPtr = _lookup< + ffi.NativeFunction Function()>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_encoding_config'); + late final _cst_new_box_autoadd_encoding_config = + _cst_new_box_autoadd_encoding_configPtr + .asFunction Function()>(); + + ffi.Pointer cst_new_box_autoadd_filter() { + return _cst_new_box_autoadd_filter(); + } + + late final _cst_new_box_autoadd_filterPtr = + _lookup Function()>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_filter'); + late final _cst_new_box_autoadd_filter = _cst_new_box_autoadd_filterPtr + .asFunction Function()>(); + + ffi.Pointer cst_new_box_autoadd_filter_config() { + return _cst_new_box_autoadd_filter_config(); + } + + late final _cst_new_box_autoadd_filter_configPtr = _lookup< + ffi.NativeFunction Function()>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_filter_config'); + late final _cst_new_box_autoadd_filter_config = + _cst_new_box_autoadd_filter_configPtr + .asFunction Function()>(); + + ffi.Pointer cst_new_box_autoadd_preprocessing( + int value, + ) { + return _cst_new_box_autoadd_preprocessing( + value, + ); + } + + late final _cst_new_box_autoadd_preprocessingPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_preprocessing'); + late final _cst_new_box_autoadd_preprocessing = + _cst_new_box_autoadd_preprocessingPtr + .asFunction Function(int)>(); + + ffi.Pointer cst_new_box_autoadd_u_8( + int value, + ) { + return _cst_new_box_autoadd_u_8( + value, + ); + } + + late final _cst_new_box_autoadd_u_8Ptr = + _lookup Function(ffi.Uint8)>>( + 'frbgen_seventv_for_whatsapp_cst_new_box_autoadd_u_8'); + late final _cst_new_box_autoadd_u_8 = _cst_new_box_autoadd_u_8Ptr + .asFunction Function(int)>(); + + ffi.Pointer cst_new_list_frame( + int len, + ) { + return _cst_new_list_frame( + len, + ); + } + + late final _cst_new_list_framePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32)>>('frbgen_seventv_for_whatsapp_cst_new_list_frame'); + late final _cst_new_list_frame = _cst_new_list_framePtr + .asFunction Function(int)>(); + + ffi.Pointer cst_new_list_prim_u_8( + int len, + ) { + return _cst_new_list_prim_u_8( + len, + ); + } + + late final _cst_new_list_prim_u_8Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32)>>('frbgen_seventv_for_whatsapp_cst_new_list_prim_u_8'); + late final _cst_new_list_prim_u_8 = _cst_new_list_prim_u_8Ptr + .asFunction Function(int)>(); + + int dummy_method_to_enforce_bundling() { + return _dummy_method_to_enforce_bundling(); + } + + late final _dummy_method_to_enforce_bundlingPtr = + _lookup>( + 'dummy_method_to_enforce_bundling'); + late final _dummy_method_to_enforce_bundling = + _dummy_method_to_enforce_bundlingPtr.asFunction(); +} + +final class wire_cst_list_prim_u_8 extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + +final class wire_cst_frame extends ffi.Struct { + external ffi.Pointer data; + + @ffi.Uint32() + external int width; + + @ffi.Uint32() + external int height; + + @ffi.Int32() + external int timestamp; +} + +final class wire_cst_list_frame extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + +final class wire_cst_filter_config extends ffi.Struct { + external ffi.Pointer strength; + + @ffi.Uint8() + external int sharpness; +} + +final class wire_cst_Filter_Simple extends ffi.Struct { + external ffi.Pointer field0; +} + +final class wire_cst_Filter_Strong extends ffi.Struct { + external ffi.Pointer field0; +} + +final class FilterKind extends ffi.Union { + external wire_cst_Filter_Simple Simple; + + external wire_cst_Filter_Strong Strong; +} + +final class wire_cst_filter extends ffi.Struct { + @ffi.Int32() + external int tag; + + external FilterKind kind; +} + +final class wire_cst_encoding_config extends ffi.Struct { + @ffi.Uint8() + external int method; + + @ffi.Bool() + external bool losless; + + @ffi.Float() + external double quality; + + @ffi.Int32() + external int target_size; + + @ffi.Float() + external double target_psnr; + + @ffi.Uint8() + external int segments; + + @ffi.Uint8() + external int noise_shaping; + + external ffi.Pointer filter; + + @ffi.Bool() + external bool alpha_compression; + + external ffi.Pointer alpha_filtering; + + @ffi.Uint8() + external int alpha_quality; + + @ffi.Uint8() + external int pass; + + @ffi.Bool() + external bool show_compressed; + + external ffi.Pointer preprocessing; + + @ffi.Uint8() + external int partitions; + + @ffi.Uint8() + external int partition_limit; + + @ffi.Bool() + external bool use_sharp_yuv; +} diff --git a/src/lib/src/rust/frb_generated.web.dart b/src/lib/src/rust/frb_generated.web.dart new file mode 100644 index 0000000..e706d2c --- /dev/null +++ b/src/lib/src/rust/frb_generated.web.dart @@ -0,0 +1,439 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables + +import 'api/api.dart'; +import 'dart:async'; +import 'dart:convert'; +import 'frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; +import 'webp/encode.dart'; +import 'webp/shared.dart'; + +abstract class RustLibApiImplPlatform extends BaseApiImpl { + RustLibApiImplPlatform({ + required super.handler, + required super.wire, + required super.generalizedFrbRustBinding, + required super.portManager, + }); + + @protected + AlphaFilter dco_decode_alpha_filter(dynamic raw); + + @protected + bool dco_decode_bool(dynamic raw); + + @protected + AlphaFilter dco_decode_box_autoadd_alpha_filter(dynamic raw); + + @protected + EncodingConfig dco_decode_box_autoadd_encoding_config(dynamic raw); + + @protected + Filter dco_decode_box_autoadd_filter(dynamic raw); + + @protected + FilterConfig dco_decode_box_autoadd_filter_config(dynamic raw); + + @protected + Preprocessing dco_decode_box_autoadd_preprocessing(dynamic raw); + + @protected + int dco_decode_box_autoadd_u_8(dynamic raw); + + @protected + EncodingConfig dco_decode_encoding_config(dynamic raw); + + @protected + double dco_decode_f_32(dynamic raw); + + @protected + Filter dco_decode_filter(dynamic raw); + + @protected + FilterConfig dco_decode_filter_config(dynamic raw); + + @protected + Frame dco_decode_frame(dynamic raw); + + @protected + int dco_decode_i_32(dynamic raw); + + @protected + List dco_decode_list_frame(dynamic raw); + + @protected + Uint8List dco_decode_list_prim_u_8(dynamic raw); + + @protected + AlphaFilter? dco_decode_opt_box_autoadd_alpha_filter(dynamic raw); + + @protected + Filter? dco_decode_opt_box_autoadd_filter(dynamic raw); + + @protected + Preprocessing? dco_decode_opt_box_autoadd_preprocessing(dynamic raw); + + @protected + int? dco_decode_opt_box_autoadd_u_8(dynamic raw); + + @protected + Preprocessing dco_decode_preprocessing(dynamic raw); + + @protected + int dco_decode_u_32(dynamic raw); + + @protected + int dco_decode_u_8(dynamic raw); + + @protected + void dco_decode_unit(dynamic raw); + + @protected + AlphaFilter sse_decode_alpha_filter(SseDeserializer deserializer); + + @protected + bool sse_decode_bool(SseDeserializer deserializer); + + @protected + AlphaFilter sse_decode_box_autoadd_alpha_filter(SseDeserializer deserializer); + + @protected + EncodingConfig sse_decode_box_autoadd_encoding_config( + SseDeserializer deserializer); + + @protected + Filter sse_decode_box_autoadd_filter(SseDeserializer deserializer); + + @protected + FilterConfig sse_decode_box_autoadd_filter_config( + SseDeserializer deserializer); + + @protected + Preprocessing sse_decode_box_autoadd_preprocessing( + SseDeserializer deserializer); + + @protected + int sse_decode_box_autoadd_u_8(SseDeserializer deserializer); + + @protected + EncodingConfig sse_decode_encoding_config(SseDeserializer deserializer); + + @protected + double sse_decode_f_32(SseDeserializer deserializer); + + @protected + Filter sse_decode_filter(SseDeserializer deserializer); + + @protected + FilterConfig sse_decode_filter_config(SseDeserializer deserializer); + + @protected + Frame sse_decode_frame(SseDeserializer deserializer); + + @protected + int sse_decode_i_32(SseDeserializer deserializer); + + @protected + List sse_decode_list_frame(SseDeserializer deserializer); + + @protected + Uint8List sse_decode_list_prim_u_8(SseDeserializer deserializer); + + @protected + AlphaFilter? sse_decode_opt_box_autoadd_alpha_filter( + SseDeserializer deserializer); + + @protected + Filter? sse_decode_opt_box_autoadd_filter(SseDeserializer deserializer); + + @protected + Preprocessing? sse_decode_opt_box_autoadd_preprocessing( + SseDeserializer deserializer); + + @protected + int? sse_decode_opt_box_autoadd_u_8(SseDeserializer deserializer); + + @protected + Preprocessing sse_decode_preprocessing(SseDeserializer deserializer); + + @protected + int sse_decode_u_32(SseDeserializer deserializer); + + @protected + int sse_decode_u_8(SseDeserializer deserializer); + + @protected + void sse_decode_unit(SseDeserializer deserializer); + + @protected + int cst_encode_box_autoadd_alpha_filter(AlphaFilter raw) { + return cst_encode_alpha_filter(raw); + } + + @protected + List cst_encode_box_autoadd_encoding_config(EncodingConfig raw) { + return cst_encode_encoding_config(raw); + } + + @protected + List cst_encode_box_autoadd_filter(Filter raw) { + return cst_encode_filter(raw); + } + + @protected + List cst_encode_box_autoadd_filter_config(FilterConfig raw) { + return cst_encode_filter_config(raw); + } + + @protected + int cst_encode_box_autoadd_preprocessing(Preprocessing raw) { + return cst_encode_preprocessing(raw); + } + + @protected + int cst_encode_box_autoadd_u_8(int raw) { + return cst_encode_u_8(raw); + } + + @protected + List cst_encode_encoding_config(EncodingConfig raw) { + return [ + cst_encode_u_8(raw.method), + cst_encode_bool(raw.losless), + cst_encode_f_32(raw.quality), + cst_encode_i_32(raw.targetSize), + cst_encode_f_32(raw.targetPsnr), + cst_encode_u_8(raw.segments), + cst_encode_u_8(raw.noiseShaping), + cst_encode_opt_box_autoadd_filter(raw.filter), + cst_encode_bool(raw.alphaCompression), + cst_encode_opt_box_autoadd_alpha_filter(raw.alphaFiltering), + cst_encode_u_8(raw.alphaQuality), + cst_encode_u_8(raw.pass), + cst_encode_bool(raw.showCompressed), + cst_encode_opt_box_autoadd_preprocessing(raw.preprocessing), + cst_encode_u_8(raw.partitions), + cst_encode_u_8(raw.partitionLimit), + cst_encode_bool(raw.useSharpYuv) + ]; + } + + @protected + List cst_encode_filter(Filter raw) { + if (raw is Filter_Simple) { + return [0, cst_encode_box_autoadd_filter_config(raw.field0)]; + } + if (raw is Filter_Strong) { + return [1, cst_encode_box_autoadd_filter_config(raw.field0)]; + } + + throw Exception('unreachable'); + } + + @protected + List cst_encode_filter_config(FilterConfig raw) { + return [ + cst_encode_opt_box_autoadd_u_8(raw.strength), + cst_encode_u_8(raw.sharpness) + ]; + } + + @protected + List cst_encode_frame(Frame raw) { + return [ + cst_encode_list_prim_u_8(raw.data), + cst_encode_u_32(raw.width), + cst_encode_u_32(raw.height), + cst_encode_i_32(raw.timestamp) + ]; + } + + @protected + List cst_encode_list_frame(List raw) { + return raw.map(cst_encode_frame).toList(); + } + + @protected + Uint8List cst_encode_list_prim_u_8(Uint8List raw) { + return raw; + } + + @protected + int? cst_encode_opt_box_autoadd_alpha_filter(AlphaFilter? raw) { + return raw == null ? null : cst_encode_box_autoadd_alpha_filter(raw); + } + + @protected + List? cst_encode_opt_box_autoadd_filter(Filter? raw) { + return raw == null ? null : cst_encode_box_autoadd_filter(raw); + } + + @protected + int? cst_encode_opt_box_autoadd_preprocessing(Preprocessing? raw) { + return raw == null ? null : cst_encode_box_autoadd_preprocessing(raw); + } + + @protected + int? cst_encode_opt_box_autoadd_u_8(int? raw) { + return raw == null ? null : cst_encode_box_autoadd_u_8(raw); + } + + @protected + int cst_encode_alpha_filter(AlphaFilter raw); + + @protected + bool cst_encode_bool(bool raw); + + @protected + double cst_encode_f_32(double raw); + + @protected + int cst_encode_i_32(int raw); + + @protected + int cst_encode_preprocessing(Preprocessing raw); + + @protected + int cst_encode_u_32(int raw); + + @protected + int cst_encode_u_8(int raw); + + @protected + void cst_encode_unit(void raw); + + @protected + void sse_encode_alpha_filter(AlphaFilter self, SseSerializer serializer); + + @protected + void sse_encode_bool(bool self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_alpha_filter( + AlphaFilter self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_encoding_config( + EncodingConfig self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_filter(Filter self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_filter_config( + FilterConfig self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_preprocessing( + Preprocessing self, SseSerializer serializer); + + @protected + void sse_encode_box_autoadd_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_encoding_config( + EncodingConfig self, SseSerializer serializer); + + @protected + void sse_encode_f_32(double self, SseSerializer serializer); + + @protected + void sse_encode_filter(Filter self, SseSerializer serializer); + + @protected + void sse_encode_filter_config(FilterConfig self, SseSerializer serializer); + + @protected + void sse_encode_frame(Frame self, SseSerializer serializer); + + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + + @protected + void sse_encode_list_frame(List self, SseSerializer serializer); + + @protected + void sse_encode_list_prim_u_8(Uint8List self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_alpha_filter( + AlphaFilter? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_filter( + Filter? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_preprocessing( + Preprocessing? self, SseSerializer serializer); + + @protected + void sse_encode_opt_box_autoadd_u_8(int? self, SseSerializer serializer); + + @protected + void sse_encode_preprocessing(Preprocessing self, SseSerializer serializer); + + @protected + void sse_encode_u_32(int self, SseSerializer serializer); + + @protected + void sse_encode_u_8(int self, SseSerializer serializer); + + @protected + void sse_encode_unit(void self, SseSerializer serializer); +} + +// Section: wire_class + +class RustLibWire extends BaseWire { + RustLibWire.fromExternalLibrary(ExternalLibrary lib); + + void dart_fn_deliver_output(int call_id, PlatformGeneralizedUint8ListPtr ptr_, + int rust_vec_len_, int data_len_) => + wasmModule.dart_fn_deliver_output( + call_id, ptr_, rust_vec_len_, data_len_); + + void wire_calc_translucency(NativePortType port_, List frames) => + wasmModule.wire_calc_translucency(port_, frames); + + void wire_encode( + NativePortType port_, List frames, List config) => + wasmModule.wire_encode(port_, frames, config); + + void wire_into_frames(NativePortType port_, Uint8List bytes) => + wasmModule.wire_into_frames(port_, bytes); + + void wire_upscale_frames_with_padding( + NativePortType port_, List frames, int width, int height) => + wasmModule.wire_upscale_frames_with_padding(port_, frames, width, height); +} + +@JS('wasm_bindgen') +external RustLibWasmModule get wasmModule; + +@JS() +@anonymous +class RustLibWasmModule implements WasmModule { + @override + external Object /* Promise */ call([String? moduleName]); + + @override + external RustLibWasmModule bind(dynamic thisArg, String moduleName); + + external void dart_fn_deliver_output(int call_id, + PlatformGeneralizedUint8ListPtr ptr_, int rust_vec_len_, int data_len_); + + external void wire_calc_translucency( + NativePortType port_, List frames); + + external void wire_encode( + NativePortType port_, List frames, List config); + + external void wire_into_frames(NativePortType port_, Uint8List bytes); + + external void wire_upscale_frames_with_padding( + NativePortType port_, List frames, int width, int height); +} diff --git a/src/lib/src/rust/webp/encode.dart b/src/lib/src/rust/webp/encode.dart new file mode 100644 index 0000000..877712b --- /dev/null +++ b/src/lib/src/rust/webp/encode.dart @@ -0,0 +1,132 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; +import 'package:freezed_annotation/freezed_annotation.dart' hide protected; +part 'encode.freezed.dart'; + +enum AlphaFilter { + fast, + best, +} + +class EncodingConfig { + final int method; + final bool losless; + final double quality; + final int targetSize; + final double targetPsnr; + final int segments; + final int noiseShaping; + final Filter? filter; + final bool alphaCompression; + final AlphaFilter? alphaFiltering; + final int alphaQuality; + final int pass; + final bool showCompressed; + final Preprocessing? preprocessing; + final int partitions; + final int partitionLimit; + final bool useSharpYuv; + + const EncodingConfig({ + required this.method, + required this.losless, + required this.quality, + required this.targetSize, + required this.targetPsnr, + required this.segments, + required this.noiseShaping, + this.filter, + required this.alphaCompression, + this.alphaFiltering, + required this.alphaQuality, + required this.pass, + required this.showCompressed, + this.preprocessing, + required this.partitions, + required this.partitionLimit, + required this.useSharpYuv, + }); + + @override + int get hashCode => + method.hashCode ^ + losless.hashCode ^ + quality.hashCode ^ + targetSize.hashCode ^ + targetPsnr.hashCode ^ + segments.hashCode ^ + noiseShaping.hashCode ^ + filter.hashCode ^ + alphaCompression.hashCode ^ + alphaFiltering.hashCode ^ + alphaQuality.hashCode ^ + pass.hashCode ^ + showCompressed.hashCode ^ + preprocessing.hashCode ^ + partitions.hashCode ^ + partitionLimit.hashCode ^ + useSharpYuv.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is EncodingConfig && + runtimeType == other.runtimeType && + method == other.method && + losless == other.losless && + quality == other.quality && + targetSize == other.targetSize && + targetPsnr == other.targetPsnr && + segments == other.segments && + noiseShaping == other.noiseShaping && + filter == other.filter && + alphaCompression == other.alphaCompression && + alphaFiltering == other.alphaFiltering && + alphaQuality == other.alphaQuality && + pass == other.pass && + showCompressed == other.showCompressed && + preprocessing == other.preprocessing && + partitions == other.partitions && + partitionLimit == other.partitionLimit && + useSharpYuv == other.useSharpYuv; +} + +@freezed +sealed class Filter with _$Filter { + const factory Filter.simple( + FilterConfig field0, + ) = Filter_Simple; + const factory Filter.strong( + FilterConfig field0, + ) = Filter_Strong; +} + +class FilterConfig { + final int? strength; + final int sharpness; + + const FilterConfig({ + this.strength, + required this.sharpness, + }); + + @override + int get hashCode => strength.hashCode ^ sharpness.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FilterConfig && + runtimeType == other.runtimeType && + strength == other.strength && + sharpness == other.sharpness; +} + +enum Preprocessing { + segmentSmooth, +} diff --git a/src/lib/models/webp_bridge_generated.freezed.dart b/src/lib/src/rust/webp/encode.freezed.dart similarity index 99% rename from src/lib/models/webp_bridge_generated.freezed.dart rename to src/lib/src/rust/webp/encode.freezed.dart index 3348b82..9e96d94 100644 --- a/src/lib/models/webp_bridge_generated.freezed.dart +++ b/src/lib/src/rust/webp/encode.freezed.dart @@ -3,7 +3,7 @@ // 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 'webp_bridge_generated.dart'; +part of 'encode.dart'; // ************************************************************************** // FreezedGenerator diff --git a/src/lib/src/rust/webp/shared.dart b/src/lib/src/rust/webp/shared.dart new file mode 100644 index 0000000..e656a3e --- /dev/null +++ b/src/lib/src/rust/webp/shared.dart @@ -0,0 +1,35 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +class Frame { + final Uint8List data; + final int width; + final int height; + final int timestamp; + + const Frame({ + required this.data, + required this.width, + required this.height, + required this.timestamp, + }); + + @override + int get hashCode => + data.hashCode ^ width.hashCode ^ height.hashCode ^ timestamp.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Frame && + runtimeType == other.runtimeType && + data == other.data && + width == other.width && + height == other.height && + timestamp == other.timestamp; +} diff --git a/src/lib/widgets/create_stickerpack_dialog.dart b/src/lib/widgets/create_stickerpack_dialog.dart index 0d0f059..e383cd8 100644 --- a/src/lib/widgets/create_stickerpack_dialog.dart +++ b/src/lib/widgets/create_stickerpack_dialog.dart @@ -6,10 +6,13 @@ class CreateStickerPackDialog extends StatefulWidget { final String defaultPublisher; const CreateStickerPackDialog( - {super.key, this.defaultName, this.defaultPublisher = '7TV for WhatsApp'}); + {super.key, + this.defaultName, + this.defaultPublisher = '7TV for WhatsApp'}); @override - State createState() => _CreateStickerPackDialogState(); + State createState() => + _CreateStickerPackDialogState(); Future show(BuildContext context) => showDialog(context: context, builder: (_) => this); @@ -44,7 +47,8 @@ class _CreateStickerPackDialogState extends State { TextFormField( autofocus: true, controller: nameController, - decoration: const InputDecoration(border: OutlineInputBorder(), labelText: 'Name'), + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: 'Name'), validator: (name) => name?.isEmpty ?? true ? "Can't be empty" : name!.length > 128 @@ -54,8 +58,8 @@ class _CreateStickerPackDialogState extends State { const SizedBox(height: 10), TextFormField( controller: publisherController, - decoration: - const InputDecoration(border: OutlineInputBorder(), labelText: 'Publisher'), + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: 'Publisher'), validator: (publisher) => publisher?.isEmpty ?? true ? "Can't be empty" : publisher!.length > 128 @@ -69,8 +73,8 @@ class _CreateStickerPackDialogState extends State { TextButton( onPressed: canCreate ? () async { - var stickerPack = - StickerPack.withDefaults(nameController.text, publisherController.text); + var stickerPack = StickerPack.withDefaults( + nameController.text, publisherController.text); await stickerPack.save(); debugPrint('created sticker pack'); if (context.mounted) { diff --git a/src/lib/widgets/emote_emoji_picker.dart b/src/lib/widgets/emote_emoji_picker.dart index f9528aa..b2ea8cf 100644 --- a/src/lib/widgets/emote_emoji_picker.dart +++ b/src/lib/widgets/emote_emoji_picker.dart @@ -92,7 +92,8 @@ class _EmoteEmojiPickerState extends State { }, config: Config( bgColor: Theme.of(context).colorScheme.background, - skinToneDialogBgColor: Theme.of(context).colorScheme.background, + skinToneDialogBgColor: + Theme.of(context).colorScheme.background, indicatorColor: Theme.of(context).colorScheme.primary, iconColor: Theme.of(context).disabledColor, iconColorSelected: Theme.of(context).colorScheme.primary, diff --git a/src/linux/flutter/generated_plugin_registrant.cc b/src/linux/flutter/generated_plugin_registrant.cc index 2b711dd..06c48a8 100644 --- a/src/linux/flutter/generated_plugin_registrant.cc +++ b/src/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) emoji_picker_flutter_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "EmojiPickerFlutterPlugin"); emoji_picker_flutter_plugin_register_with_registrar(emoji_picker_flutter_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/src/linux/flutter/generated_plugins.cmake b/src/linux/flutter/generated_plugins.cmake index 69cf212..4bc1685 100644 --- a/src/linux/flutter/generated_plugins.cmake +++ b/src/linux/flutter/generated_plugins.cmake @@ -4,9 +4,11 @@ list(APPEND FLUTTER_PLUGIN_LIST emoji_picker_flutter + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + rust_builder ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/src/macos/Flutter/GeneratedPluginRegistrant.swift b/src/macos/Flutter/GeneratedPluginRegistrant.swift index 431c7b2..ad26d62 100644 --- a/src/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/src/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,10 +9,12 @@ import emoji_picker_flutter import flutter_local_notifications import path_provider_foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/src/pubspec.lock b/src/pubspec.lock index f2e717a..c1b3b8d 100644 --- a/src/pubspec.lock +++ b/src/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: @@ -298,10 +298,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: bb5cd63ff7c91d6efe452e41d0d0ae6348925c82eafd10ce170ef585ea04776e + sha256: "892ada16046d641263f30c72e7432397088810a84f34479f6677494802a2b535" url: "https://pub.dev" source: hosted - version: "16.2.0" + version: "16.3.0" flutter_local_notifications_linux: dependency: transitive description: @@ -322,10 +322,10 @@ packages: dependency: "direct main" description: name: flutter_rust_bridge - sha256: ab049242043fd1f5df25534774a2dfa586286d0577b090c22d4f5ed30c366143 + sha256: "657e31267e98b4d187a96798476d267e16d061ac3acbca6296d2580eaabd0ec7" url: "https://pub.dev" source: hosted - version: "1.82.5" + version: "2.0.0-dev.11" flutter_test: dependency: "direct dev" description: flutter @@ -401,14 +401,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" - http: - dependency: transitive - description: - name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 - url: "https://pub.dev" - source: hosted - version: "1.1.2" http_multi_server: dependency: transitive description: @@ -541,10 +533,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: @@ -589,10 +581,10 @@ packages: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: @@ -633,14 +625,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" - puppeteer: - dependency: transitive - description: - name: puppeteer - sha256: eedeaae6ec5d2e54f9ae22ab4d6b3dda2e8791c356cc783046d06c287ffe11d8 - url: "https://pub.dev" - source: hosted - version: "3.6.0" quiver: dependency: transitive description: @@ -649,6 +633,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + rust_builder: + dependency: "direct main" + description: + path: rust_builder + relative: true + source: path + version: "0.0.1" shared_preferences: dependency: "direct main" description: @@ -713,14 +704,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" shelf_web_socket: dependency: transitive description: @@ -758,14 +741,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" stack_trace: dependency: transitive description: @@ -830,14 +805,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" - tuple: - dependency: transitive - description: - name: tuple - sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 - url: "https://pub.dev" - source: hosted - version: "2.0.2" typed_data: dependency: transitive description: @@ -926,14 +893,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" - uuid: - dependency: transitive - description: - name: uuid - sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" - url: "https://pub.dev" - source: hosted - version: "4.2.2" vector_math: dependency: transitive description: diff --git a/src/pubspec.yaml b/src/pubspec.yaml index dc7ee92..78a31d2 100644 --- a/src/pubspec.yaml +++ b/src/pubspec.yaml @@ -16,13 +16,15 @@ dependencies: image: ^4.0.13 shared_preferences: ^2.0.15 ffi: ^2.0.1 - flutter_rust_bridge: ^1.59.0 + flutter_rust_bridge: ^2.0.0-dev.11 freezed_annotation: ^2.2.0 ulid: ^2.0.0 flutter_local_notifications: ^16.2.0 json_annotation: ^4.8.0 get_it: ^7.2.0 url_launcher: ^6.2.2 + rust_builder: + path: rust_builder dev_dependencies: flutter_test: sdk: flutter diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 3c328f5..21aa5b4 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -4,12 +4,10 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["staticlib", "cdylib"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +crate-type = ["cdylib", "staticlib"] [dependencies] -flutter_rust_bridge = "1" +flutter_rust_bridge = "=2.0.0-dev.11" webp = {git = "https://github.com/jaredforth/webp.git"} image = "0.24.5" fast_image_resize = "2.6.0" \ No newline at end of file diff --git a/src/rust/src/api.rs b/src/rust/src/api/api.rs similarity index 100% rename from src/rust/src/api.rs rename to src/rust/src/api/api.rs diff --git a/src/rust/src/api/mod.rs b/src/rust/src/api/mod.rs new file mode 100644 index 0000000..c78317c --- /dev/null +++ b/src/rust/src/api/mod.rs @@ -0,0 +1 @@ +pub mod api; \ No newline at end of file diff --git a/src/rust/src/bridge_generated.io.rs b/src/rust/src/bridge_generated.io.rs deleted file mode 100644 index 589cb77..0000000 --- a/src/rust/src/bridge_generated.io.rs +++ /dev/null @@ -1,393 +0,0 @@ -use super::*; -// Section: wire functions - -#[no_mangle] -pub extern "C" fn wire_into_frames(port_: i64, bytes: *mut wire_uint_8_list) { - wire_into_frames_impl(port_, bytes) -} - -#[no_mangle] -pub extern "C" fn wire_upscale_frames_with_padding( - port_: i64, - frames: *mut wire_list_frame, - width: u32, - height: u32, -) { - wire_upscale_frames_with_padding_impl(port_, frames, width, height) -} - -#[no_mangle] -pub extern "C" fn wire_encode( - port_: i64, - frames: *mut wire_list_frame, - config: *mut wire_EncodingConfig, -) { - wire_encode_impl(port_, frames, config) -} - -#[no_mangle] -pub extern "C" fn wire_calc_translucency(port_: i64, frames: *mut wire_list_frame) { - wire_calc_translucency_impl(port_, frames) -} - -// Section: allocate functions - -#[no_mangle] -pub extern "C" fn new_box_autoadd_alpha_filter_0(value: i32) -> *mut i32 { - support::new_leak_box_ptr(value) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_encoding_config_0() -> *mut wire_EncodingConfig { - support::new_leak_box_ptr(wire_EncodingConfig::new_with_null_ptr()) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_filter_0() -> *mut wire_Filter { - support::new_leak_box_ptr(wire_Filter::new_with_null_ptr()) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_filter_config_0() -> *mut wire_FilterConfig { - support::new_leak_box_ptr(wire_FilterConfig::new_with_null_ptr()) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_preprocessing_0(value: i32) -> *mut i32 { - support::new_leak_box_ptr(value) -} - -#[no_mangle] -pub extern "C" fn new_box_autoadd_u8_0(value: u8) -> *mut u8 { - support::new_leak_box_ptr(value) -} - -#[no_mangle] -pub extern "C" fn new_list_frame_0(len: i32) -> *mut wire_list_frame { - let wrap = wire_list_frame { - ptr: support::new_leak_vec_ptr(::new_with_null_ptr(), len), - len, - }; - support::new_leak_box_ptr(wrap) -} - -#[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: impl Wire2Api - -impl Wire2Api for *mut i32 { - fn wire2api(self) -> AlphaFilter { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - Wire2Api::::wire2api(*wrap).into() - } -} -impl Wire2Api for *mut wire_EncodingConfig { - fn wire2api(self) -> EncodingConfig { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - Wire2Api::::wire2api(*wrap).into() - } -} -impl Wire2Api for *mut wire_Filter { - fn wire2api(self) -> Filter { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - Wire2Api::::wire2api(*wrap).into() - } -} -impl Wire2Api for *mut wire_FilterConfig { - fn wire2api(self) -> FilterConfig { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - Wire2Api::::wire2api(*wrap).into() - } -} -impl Wire2Api for *mut i32 { - fn wire2api(self) -> Preprocessing { - let wrap = unsafe { support::box_from_leak_ptr(self) }; - Wire2Api::::wire2api(*wrap).into() - } -} -impl Wire2Api for *mut u8 { - fn wire2api(self) -> u8 { - unsafe { *support::box_from_leak_ptr(self) } - } -} -impl Wire2Api for wire_EncodingConfig { - fn wire2api(self) -> EncodingConfig { - EncodingConfig { - method: self.method.wire2api(), - losless: self.losless.wire2api(), - quality: self.quality.wire2api(), - target_size: self.target_size.wire2api(), - target_psnr: self.target_psnr.wire2api(), - segments: self.segments.wire2api(), - noise_shaping: self.noise_shaping.wire2api(), - filter: self.filter.wire2api(), - alpha_compression: self.alpha_compression.wire2api(), - alpha_filtering: self.alpha_filtering.wire2api(), - alpha_quality: self.alpha_quality.wire2api(), - pass: self.pass.wire2api(), - show_compressed: self.show_compressed.wire2api(), - preprocessing: self.preprocessing.wire2api(), - partitions: self.partitions.wire2api(), - partition_limit: self.partition_limit.wire2api(), - use_sharp_yuv: self.use_sharp_yuv.wire2api(), - } - } -} - -impl Wire2Api for wire_Filter { - fn wire2api(self) -> Filter { - match self.tag { - 0 => unsafe { - let ans = support::box_from_leak_ptr(self.kind); - let ans = support::box_from_leak_ptr(ans.Simple); - Filter::Simple(ans.field0.wire2api()) - }, - 1 => unsafe { - let ans = support::box_from_leak_ptr(self.kind); - let ans = support::box_from_leak_ptr(ans.Strong); - Filter::Strong(ans.field0.wire2api()) - }, - _ => unreachable!(), - } - } -} -impl Wire2Api for wire_FilterConfig { - fn wire2api(self) -> FilterConfig { - FilterConfig { - strength: self.strength.wire2api(), - sharpness: self.sharpness.wire2api(), - } - } -} -impl Wire2Api for wire_Frame { - fn wire2api(self) -> Frame { - Frame { - data: self.data.wire2api(), - width: self.width.wire2api(), - height: self.height.wire2api(), - timestamp: self.timestamp.wire2api(), - } - } -} - -impl Wire2Api> for *mut wire_list_frame { - fn wire2api(self) -> Vec { - let vec = unsafe { - let wrap = support::box_from_leak_ptr(self); - support::vec_from_leak_ptr(wrap.ptr, wrap.len) - }; - vec.into_iter().map(Wire2Api::wire2api).collect() - } -} - -impl Wire2Api> for *mut wire_uint_8_list { - fn wire2api(self) -> Vec { - unsafe { - let wrap = support::box_from_leak_ptr(self); - support::vec_from_leak_ptr(wrap.ptr, wrap.len) - } - } -} -// Section: wire structs - -#[repr(C)] -#[derive(Clone)] -pub struct wire_EncodingConfig { - method: u8, - losless: bool, - quality: f32, - target_size: i32, - target_psnr: f32, - segments: u8, - noise_shaping: u8, - filter: *mut wire_Filter, - alpha_compression: bool, - alpha_filtering: *mut i32, - alpha_quality: u8, - pass: u8, - show_compressed: bool, - preprocessing: *mut i32, - partitions: u8, - partition_limit: u8, - use_sharp_yuv: bool, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_FilterConfig { - strength: *mut u8, - sharpness: u8, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_Frame { - data: *mut wire_uint_8_list, - width: u32, - height: u32, - timestamp: i32, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_list_frame { - ptr: *mut wire_Frame, - len: i32, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_uint_8_list { - ptr: *mut u8, - len: i32, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_Filter { - tag: i32, - kind: *mut FilterKind, -} - -#[repr(C)] -pub union FilterKind { - Simple: *mut wire_Filter_Simple, - Strong: *mut wire_Filter_Strong, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_Filter_Simple { - field0: *mut wire_FilterConfig, -} - -#[repr(C)] -#[derive(Clone)] -pub struct wire_Filter_Strong { - field0: *mut wire_FilterConfig, -} - -// Section: impl NewWithNullPtr - -pub trait NewWithNullPtr { - fn new_with_null_ptr() -> Self; -} - -impl NewWithNullPtr for *mut T { - fn new_with_null_ptr() -> Self { - std::ptr::null_mut() - } -} - -impl NewWithNullPtr for wire_EncodingConfig { - fn new_with_null_ptr() -> Self { - Self { - method: Default::default(), - losless: Default::default(), - quality: Default::default(), - target_size: Default::default(), - target_psnr: Default::default(), - segments: Default::default(), - noise_shaping: Default::default(), - filter: core::ptr::null_mut(), - alpha_compression: Default::default(), - alpha_filtering: core::ptr::null_mut(), - alpha_quality: Default::default(), - pass: Default::default(), - show_compressed: Default::default(), - preprocessing: core::ptr::null_mut(), - partitions: Default::default(), - partition_limit: Default::default(), - use_sharp_yuv: Default::default(), - } - } -} - -impl Default for wire_EncodingConfig { - fn default() -> Self { - Self::new_with_null_ptr() - } -} - -impl Default for wire_Filter { - fn default() -> Self { - Self::new_with_null_ptr() - } -} - -impl NewWithNullPtr for wire_Filter { - fn new_with_null_ptr() -> Self { - Self { - tag: -1, - kind: core::ptr::null_mut(), - } - } -} - -#[no_mangle] -pub extern "C" fn inflate_Filter_Simple() -> *mut FilterKind { - support::new_leak_box_ptr(FilterKind { - Simple: support::new_leak_box_ptr(wire_Filter_Simple { - field0: core::ptr::null_mut(), - }), - }) -} - -#[no_mangle] -pub extern "C" fn inflate_Filter_Strong() -> *mut FilterKind { - support::new_leak_box_ptr(FilterKind { - Strong: support::new_leak_box_ptr(wire_Filter_Strong { - field0: core::ptr::null_mut(), - }), - }) -} - -impl NewWithNullPtr for wire_FilterConfig { - fn new_with_null_ptr() -> Self { - Self { - strength: core::ptr::null_mut(), - sharpness: Default::default(), - } - } -} - -impl Default for wire_FilterConfig { - fn default() -> Self { - Self::new_with_null_ptr() - } -} - -impl NewWithNullPtr for wire_Frame { - fn new_with_null_ptr() -> Self { - Self { - data: core::ptr::null_mut(), - width: Default::default(), - height: Default::default(), - timestamp: Default::default(), - } - } -} - -impl Default for wire_Frame { - fn default() -> Self { - Self::new_with_null_ptr() - } -} - -// Section: sync execution mode utility - -#[no_mangle] -pub extern "C" fn free_WireSyncReturn(ptr: support::WireSyncReturn) { - unsafe { - let _ = support::box_from_leak_ptr(ptr); - }; -} diff --git a/src/rust/src/bridge_generated.rs b/src/rust/src/bridge_generated.rs deleted file mode 100644 index 8a8a37d..0000000 --- a/src/rust/src/bridge_generated.rs +++ /dev/null @@ -1,198 +0,0 @@ -#![allow( - non_camel_case_types, - unused, - clippy::redundant_closure, - clippy::useless_conversion, - clippy::unit_arg, - clippy::double_parens, - non_snake_case, - clippy::too_many_arguments -)] -// AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.82.5. - -use crate::api::*; -use core::panic::UnwindSafe; -use flutter_rust_bridge::rust2dart::IntoIntoDart; -use flutter_rust_bridge::*; -use std::ffi::c_void; -use std::sync::Arc; - -// Section: imports - -use crate::webp::encode::AlphaFilter; -use crate::webp::encode::EncodingConfig; -use crate::webp::encode::Filter; -use crate::webp::encode::FilterConfig; -use crate::webp::encode::Preprocessing; -use crate::webp::shared::Frame; - -// Section: wire functions - -fn wire_into_frames_impl(port_: MessagePort, bytes: impl Wire2Api> + UnwindSafe) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Vec, _>( - WrapInfo { - debug_name: "into_frames", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || { - let api_bytes = bytes.wire2api(); - move |task_callback| Result::<_, ()>::Ok(into_frames(api_bytes)) - }, - ) -} -fn wire_upscale_frames_with_padding_impl( - port_: MessagePort, - frames: impl Wire2Api> + UnwindSafe, - width: impl Wire2Api + UnwindSafe, - height: impl Wire2Api + UnwindSafe, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Vec, _>( - WrapInfo { - debug_name: "upscale_frames_with_padding", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || { - let api_frames = frames.wire2api(); - let api_width = width.wire2api(); - let api_height = height.wire2api(); - move |task_callback| { - Result::<_, ()>::Ok(upscale_frames_with_padding( - api_frames, api_width, api_height, - )) - } - }, - ) -} -fn wire_encode_impl( - port_: MessagePort, - frames: impl Wire2Api> + UnwindSafe, - config: impl Wire2Api + UnwindSafe, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Vec, _>( - WrapInfo { - debug_name: "encode", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || { - let api_frames = frames.wire2api(); - let api_config = config.wire2api(); - move |task_callback| Result::<_, ()>::Ok(encode(api_frames, api_config)) - }, - ) -} -fn wire_calc_translucency_impl(port_: MessagePort, frames: impl Wire2Api> + UnwindSafe) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, f32, _>( - WrapInfo { - debug_name: "calc_translucency", - port: Some(port_), - mode: FfiCallMode::Normal, - }, - move || { - let api_frames = frames.wire2api(); - move |task_callback| Result::<_, ()>::Ok(calc_translucency(api_frames)) - }, - ) -} -// Section: wrapper structs - -// Section: static checks - -// Section: allocate functions - -// Section: related functions - -// Section: impl Wire2Api - -pub trait Wire2Api { - fn wire2api(self) -> T; -} - -impl Wire2Api> for *mut S -where - *mut S: Wire2Api, -{ - fn wire2api(self) -> Option { - (!self.is_null()).then(|| self.wire2api()) - } -} -impl Wire2Api for i32 { - fn wire2api(self) -> AlphaFilter { - match self { - 0 => AlphaFilter::Fast, - 1 => AlphaFilter::Best, - _ => unreachable!("Invalid variant for AlphaFilter: {}", self), - } - } -} -impl Wire2Api for bool { - fn wire2api(self) -> bool { - self - } -} - -impl Wire2Api for f32 { - fn wire2api(self) -> f32 { - self - } -} - -impl Wire2Api for i32 { - fn wire2api(self) -> i32 { - self - } -} - -impl Wire2Api for i32 { - fn wire2api(self) -> Preprocessing { - match self { - 0 => Preprocessing::SegmentSmooth, - _ => unreachable!("Invalid variant for Preprocessing: {}", self), - } - } -} -impl Wire2Api for u32 { - fn wire2api(self) -> u32 { - self - } -} -impl Wire2Api for u8 { - fn wire2api(self) -> u8 { - self - } -} - -// Section: impl IntoDart - -impl support::IntoDart for Frame { - fn into_dart(self) -> support::DartAbi { - vec![ - self.data.into_into_dart().into_dart(), - self.width.into_into_dart().into_dart(), - self.height.into_into_dart().into_dart(), - self.timestamp.into_into_dart().into_dart(), - ] - .into_dart() - } -} -impl support::IntoDartExceptPrimitive for Frame {} -impl rust2dart::IntoIntoDart for Frame { - fn into_into_dart(self) -> Self { - self - } -} - -// Section: executor - -support::lazy_static! { - pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); -} - -#[cfg(not(target_family = "wasm"))] -#[path = "bridge_generated.io.rs"] -mod io; -#[cfg(not(target_family = "wasm"))] -pub use self::io::*; diff --git a/src/rust/src/frb_generated.io.rs b/src/rust/src/frb_generated.io.rs new file mode 100644 index 0000000..3110717 --- /dev/null +++ b/src/rust/src/frb_generated.io.rs @@ -0,0 +1,384 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// Section: imports + +use super::*; +use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use flutter_rust_bridge::for_generated::transform_result_dco; +use flutter_rust_bridge::{Handler, IntoIntoDart}; + +// Section: dart2rust + +impl CstDecode for *mut i32 { + fn cst_decode(self) -> crate::webp::encode::AlphaFilter { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } +} +impl CstDecode for *mut wire_cst_encoding_config { + fn cst_decode(self) -> crate::webp::encode::EncodingConfig { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } +} +impl CstDecode for *mut wire_cst_filter { + fn cst_decode(self) -> crate::webp::encode::Filter { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } +} +impl CstDecode for *mut wire_cst_filter_config { + fn cst_decode(self) -> crate::webp::encode::FilterConfig { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } +} +impl CstDecode for *mut i32 { + fn cst_decode(self) -> crate::webp::encode::Preprocessing { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } +} +impl CstDecode for *mut u8 { + fn cst_decode(self) -> u8 { + unsafe { *flutter_rust_bridge::for_generated::box_from_leak_ptr(self) } + } +} +impl CstDecode for wire_cst_encoding_config { + fn cst_decode(self) -> crate::webp::encode::EncodingConfig { + crate::webp::encode::EncodingConfig { + method: self.method.cst_decode(), + losless: self.losless.cst_decode(), + quality: self.quality.cst_decode(), + target_size: self.target_size.cst_decode(), + target_psnr: self.target_psnr.cst_decode(), + segments: self.segments.cst_decode(), + noise_shaping: self.noise_shaping.cst_decode(), + filter: self.filter.cst_decode(), + alpha_compression: self.alpha_compression.cst_decode(), + alpha_filtering: self.alpha_filtering.cst_decode(), + alpha_quality: self.alpha_quality.cst_decode(), + pass: self.pass.cst_decode(), + show_compressed: self.show_compressed.cst_decode(), + preprocessing: self.preprocessing.cst_decode(), + partitions: self.partitions.cst_decode(), + partition_limit: self.partition_limit.cst_decode(), + use_sharp_yuv: self.use_sharp_yuv.cst_decode(), + } + } +} +impl CstDecode for wire_cst_filter { + fn cst_decode(self) -> crate::webp::encode::Filter { + match self.tag { + 0 => { + let ans = unsafe { self.kind.Simple }; + crate::webp::encode::Filter::Simple(ans.field0.cst_decode()) + } + 1 => { + let ans = unsafe { self.kind.Strong }; + crate::webp::encode::Filter::Strong(ans.field0.cst_decode()) + } + _ => unreachable!(), + } + } +} +impl CstDecode for wire_cst_filter_config { + fn cst_decode(self) -> crate::webp::encode::FilterConfig { + crate::webp::encode::FilterConfig { + strength: self.strength.cst_decode(), + sharpness: self.sharpness.cst_decode(), + } + } +} +impl CstDecode for wire_cst_frame { + fn cst_decode(self) -> crate::webp::shared::Frame { + crate::webp::shared::Frame { + data: self.data.cst_decode(), + width: self.width.cst_decode(), + height: self.height.cst_decode(), + timestamp: self.timestamp.cst_decode(), + } + } +} +impl CstDecode> for *mut wire_cst_list_frame { + fn cst_decode(self) -> Vec { + let vec = unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(CstDecode::cst_decode).collect() + } +} +impl CstDecode> for *mut wire_cst_list_prim_u_8 { + fn cst_decode(self) -> Vec { + unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + } + } +} +pub trait NewWithNullPtr { + fn new_with_null_ptr() -> Self; +} + +impl NewWithNullPtr for *mut T { + fn new_with_null_ptr() -> Self { + std::ptr::null_mut() + } +} +impl NewWithNullPtr for wire_cst_encoding_config { + fn new_with_null_ptr() -> Self { + Self { + method: Default::default(), + losless: Default::default(), + quality: Default::default(), + target_size: Default::default(), + target_psnr: Default::default(), + segments: Default::default(), + noise_shaping: Default::default(), + filter: core::ptr::null_mut(), + alpha_compression: Default::default(), + alpha_filtering: core::ptr::null_mut(), + alpha_quality: Default::default(), + pass: Default::default(), + show_compressed: Default::default(), + preprocessing: core::ptr::null_mut(), + partitions: Default::default(), + partition_limit: Default::default(), + use_sharp_yuv: Default::default(), + } + } +} +impl Default for wire_cst_encoding_config { + fn default() -> Self { + Self::new_with_null_ptr() + } +} +impl NewWithNullPtr for wire_cst_filter { + fn new_with_null_ptr() -> Self { + Self { + tag: -1, + kind: FilterKind { nil__: () }, + } + } +} +impl Default for wire_cst_filter { + fn default() -> Self { + Self::new_with_null_ptr() + } +} +impl NewWithNullPtr for wire_cst_filter_config { + fn new_with_null_ptr() -> Self { + Self { + strength: core::ptr::null_mut(), + sharpness: Default::default(), + } + } +} +impl Default for wire_cst_filter_config { + fn default() -> Self { + Self::new_with_null_ptr() + } +} +impl NewWithNullPtr for wire_cst_frame { + fn new_with_null_ptr() -> Self { + Self { + data: core::ptr::null_mut(), + width: Default::default(), + height: Default::default(), + timestamp: Default::default(), + } + } +} +impl Default for wire_cst_frame { + fn default() -> Self { + Self::new_with_null_ptr() + } +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_dart_fn_deliver_output( + call_id: i32, + ptr_: *mut u8, + rust_vec_len_: i32, + data_len_: i32, +) { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + FLUTTER_RUST_BRIDGE_HANDLER.dart_fn_handle_output(call_id, message) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_wire_calc_translucency( + port_: i64, + frames: *mut wire_cst_list_frame, +) { + wire_calc_translucency_impl(port_, frames) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_wire_encode( + port_: i64, + frames: *mut wire_cst_list_frame, + config: *mut wire_cst_encoding_config, +) { + wire_encode_impl(port_, frames, config) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_wire_into_frames( + port_: i64, + bytes: *mut wire_cst_list_prim_u_8, +) { + wire_into_frames_impl(port_, bytes) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_wire_upscale_frames_with_padding( + port_: i64, + frames: *mut wire_cst_list_frame, + width: u32, + height: u32, +) { + wire_upscale_frames_with_padding_impl(port_, frames, width, height) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_alpha_filter( + value: i32, +) -> *mut i32 { + flutter_rust_bridge::for_generated::new_leak_box_ptr(value) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_encoding_config( +) -> *mut wire_cst_encoding_config { + flutter_rust_bridge::for_generated::new_leak_box_ptr( + wire_cst_encoding_config::new_with_null_ptr(), + ) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_filter() -> *mut wire_cst_filter { + flutter_rust_bridge::for_generated::new_leak_box_ptr(wire_cst_filter::new_with_null_ptr()) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_filter_config( +) -> *mut wire_cst_filter_config { + flutter_rust_bridge::for_generated::new_leak_box_ptr(wire_cst_filter_config::new_with_null_ptr()) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_preprocessing( + value: i32, +) -> *mut i32 { + flutter_rust_bridge::for_generated::new_leak_box_ptr(value) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_box_autoadd_u_8(value: u8) -> *mut u8 { + flutter_rust_bridge::for_generated::new_leak_box_ptr(value) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_list_frame( + len: i32, +) -> *mut wire_cst_list_frame { + let wrap = wire_cst_list_frame { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr( + ::new_with_null_ptr(), + len, + ), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) +} + +#[no_mangle] +pub extern "C" fn frbgen_seventv_for_whatsapp_cst_new_list_prim_u_8( + len: i32, +) -> *mut wire_cst_list_prim_u_8 { + let ans = wire_cst_list_prim_u_8 { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(ans) +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_encoding_config { + method: u8, + losless: bool, + quality: f32, + target_size: i32, + target_psnr: f32, + segments: u8, + noise_shaping: u8, + filter: *mut wire_cst_filter, + alpha_compression: bool, + alpha_filtering: *mut i32, + alpha_quality: u8, + pass: u8, + show_compressed: bool, + preprocessing: *mut i32, + partitions: u8, + partition_limit: u8, + use_sharp_yuv: bool, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_filter { + tag: i32, + kind: FilterKind, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub union FilterKind { + Simple: wire_cst_Filter_Simple, + Strong: wire_cst_Filter_Strong, + nil__: (), +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_Filter_Simple { + field0: *mut wire_cst_filter_config, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_Filter_Strong { + field0: *mut wire_cst_filter_config, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_filter_config { + strength: *mut u8, + sharpness: u8, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_frame { + data: *mut wire_cst_list_prim_u_8, + width: u32, + height: u32, + timestamp: i32, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_list_frame { + ptr: *mut wire_cst_frame, + len: i32, +} +#[repr(C)] +#[derive(Clone, Copy)] +pub struct wire_cst_list_prim_u_8 { + ptr: *mut u8, + len: i32, +} diff --git a/src/rust/src/frb_generated.rs b/src/rust/src/frb_generated.rs new file mode 100644 index 0000000..06cdb6a --- /dev/null +++ b/src/rust/src/frb_generated.rs @@ -0,0 +1,674 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +#![allow( + non_camel_case_types, + unused, + non_snake_case, + clippy::needless_return, + clippy::redundant_closure_call, + clippy::redundant_closure, + clippy::useless_conversion, + clippy::unit_arg, + clippy::unused_unit, + clippy::double_parens, + clippy::let_and_return, + clippy::too_many_arguments +)] + +// Section: imports + +use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use flutter_rust_bridge::for_generated::transform_result_dco; +use flutter_rust_bridge::{Handler, IntoIntoDart}; + +// Section: boilerplate + +flutter_rust_bridge::frb_generated_boilerplate!(); + +// Section: executor + +flutter_rust_bridge::frb_generated_default_handler!(); + +// Section: wire_funcs + +fn wire_calc_translucency_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: impl CstDecode>, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "calc_translucency", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_frames = frames.cst_decode(); + move |context| { + transform_result_dco((move || { + Result::<_, ()>::Ok(crate::api::api::calc_translucency(api_frames)) + })()) + } + }, + ) +} +fn wire_encode_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: impl CstDecode>, + config: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "encode", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_frames = frames.cst_decode(); + let api_config = config.cst_decode(); + move |context| { + transform_result_dco((move || { + Result::<_, ()>::Ok(crate::api::api::encode(api_frames, api_config)) + })()) + } + }, + ) +} +fn wire_into_frames_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + bytes: impl CstDecode>, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "into_frames", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_bytes = bytes.cst_decode(); + move |context| { + transform_result_dco((move || { + Result::<_, ()>::Ok(crate::api::api::into_frames(api_bytes)) + })()) + } + }, + ) +} +fn wire_upscale_frames_with_padding_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: impl CstDecode>, + width: impl CstDecode, + height: impl CstDecode, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "upscale_frames_with_padding", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let api_frames = frames.cst_decode(); + let api_width = width.cst_decode(); + let api_height = height.cst_decode(); + move |context| { + transform_result_dco((move || { + Result::<_, ()>::Ok(crate::api::api::upscale_frames_with_padding( + api_frames, api_width, api_height, + )) + })()) + } + }, + ) +} + +// Section: dart2rust + +impl CstDecode for i32 { + fn cst_decode(self) -> crate::webp::encode::AlphaFilter { + match self { + 0 => crate::webp::encode::AlphaFilter::Fast, + 1 => crate::webp::encode::AlphaFilter::Best, + _ => unreachable!("Invalid variant for AlphaFilter: {}", self), + } + } +} +impl CstDecode for bool { + fn cst_decode(self) -> bool { + self + } +} +impl CstDecode for f32 { + fn cst_decode(self) -> f32 { + self + } +} +impl CstDecode for i32 { + fn cst_decode(self) -> i32 { + self + } +} +impl CstDecode for i32 { + fn cst_decode(self) -> crate::webp::encode::Preprocessing { + match self { + 0 => crate::webp::encode::Preprocessing::SegmentSmooth, + _ => unreachable!("Invalid variant for Preprocessing: {}", self), + } + } +} +impl CstDecode for u32 { + fn cst_decode(self) -> u32 { + self + } +} +impl CstDecode for u8 { + fn cst_decode(self) -> u8 { + self + } +} +impl SseDecode for crate::webp::encode::AlphaFilter { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return match inner { + 0 => crate::webp::encode::AlphaFilter::Fast, + 1 => crate::webp::encode::AlphaFilter::Best, + _ => unreachable!("Invalid variant for AlphaFilter: {}", inner), + }; + } +} + +impl SseDecode for bool { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() != 0 + } +} + +impl SseDecode for crate::webp::encode::EncodingConfig { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_method = ::sse_decode(deserializer); + let mut var_losless = ::sse_decode(deserializer); + let mut var_quality = ::sse_decode(deserializer); + let mut var_targetSize = ::sse_decode(deserializer); + let mut var_targetPsnr = ::sse_decode(deserializer); + let mut var_segments = ::sse_decode(deserializer); + let mut var_noiseShaping = ::sse_decode(deserializer); + let mut var_filter = >::sse_decode(deserializer); + let mut var_alphaCompression = ::sse_decode(deserializer); + let mut var_alphaFiltering = + >::sse_decode(deserializer); + let mut var_alphaQuality = ::sse_decode(deserializer); + let mut var_pass = ::sse_decode(deserializer); + let mut var_showCompressed = ::sse_decode(deserializer); + let mut var_preprocessing = + >::sse_decode(deserializer); + let mut var_partitions = ::sse_decode(deserializer); + let mut var_partitionLimit = ::sse_decode(deserializer); + let mut var_useSharpYuv = ::sse_decode(deserializer); + return crate::webp::encode::EncodingConfig { + method: var_method, + losless: var_losless, + quality: var_quality, + target_size: var_targetSize, + target_psnr: var_targetPsnr, + segments: var_segments, + noise_shaping: var_noiseShaping, + filter: var_filter, + alpha_compression: var_alphaCompression, + alpha_filtering: var_alphaFiltering, + alpha_quality: var_alphaQuality, + pass: var_pass, + show_compressed: var_showCompressed, + preprocessing: var_preprocessing, + partitions: var_partitions, + partition_limit: var_partitionLimit, + use_sharp_yuv: var_useSharpYuv, + }; + } +} + +impl SseDecode for f32 { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_f32::().unwrap() + } +} + +impl SseDecode for crate::webp::encode::Filter { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut tag_ = ::sse_decode(deserializer); + match tag_ { + 0 => { + let mut var_field0 = ::sse_decode(deserializer); + return crate::webp::encode::Filter::Simple(var_field0); + } + 1 => { + let mut var_field0 = ::sse_decode(deserializer); + return crate::webp::encode::Filter::Strong(var_field0); + } + _ => { + unimplemented!(""); + } + } + } +} + +impl SseDecode for crate::webp::encode::FilterConfig { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_strength = >::sse_decode(deserializer); + let mut var_sharpness = ::sse_decode(deserializer); + return crate::webp::encode::FilterConfig { + strength: var_strength, + sharpness: var_sharpness, + }; + } +} + +impl SseDecode for crate::webp::shared::Frame { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_data = >::sse_decode(deserializer); + let mut var_width = ::sse_decode(deserializer); + let mut var_height = ::sse_decode(deserializer); + let mut var_timestamp = ::sse_decode(deserializer); + return crate::webp::shared::Frame { + data: var_data, + width: var_width, + height: var_height, + timestamp: var_timestamp, + }; + } +} + +impl SseDecode for i32 { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_i32::().unwrap() + } +} + +impl SseDecode for Vec { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + +impl SseDecode for Vec { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + +impl SseDecode for Option { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + +impl SseDecode for Option { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + +impl SseDecode for Option { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode( + deserializer, + )); + } else { + return None; + } + } +} + +impl SseDecode for Option { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + +impl SseDecode for crate::webp::encode::Preprocessing { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return match inner { + 0 => crate::webp::encode::Preprocessing::SegmentSmooth, + _ => unreachable!("Invalid variant for Preprocessing: {}", inner), + }; + } +} + +impl SseDecode for u32 { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u32::().unwrap() + } +} + +impl SseDecode for u8 { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + deserializer.cursor.read_u8().unwrap() + } +} + +impl SseDecode for () { + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} +} + +// Section: rust2dart + +impl flutter_rust_bridge::IntoDart for crate::webp::encode::AlphaFilter { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + match self { + Self::Fast => 0, + Self::Best => 1, + } + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::webp::encode::AlphaFilter +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::webp::encode::AlphaFilter +{ + fn into_into_dart(self) -> crate::webp::encode::AlphaFilter { + self + } +} +impl flutter_rust_bridge::IntoDart for crate::webp::encode::EncodingConfig { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + vec![ + self.method.into_into_dart().into_dart(), + self.losless.into_into_dart().into_dart(), + self.quality.into_into_dart().into_dart(), + self.target_size.into_into_dart().into_dart(), + self.target_psnr.into_into_dart().into_dart(), + self.segments.into_into_dart().into_dart(), + self.noise_shaping.into_into_dart().into_dart(), + self.filter.into_into_dart().into_dart(), + self.alpha_compression.into_into_dart().into_dart(), + self.alpha_filtering.into_into_dart().into_dart(), + self.alpha_quality.into_into_dart().into_dart(), + self.pass.into_into_dart().into_dart(), + self.show_compressed.into_into_dart().into_dart(), + self.preprocessing.into_into_dart().into_dart(), + self.partitions.into_into_dart().into_dart(), + self.partition_limit.into_into_dart().into_dart(), + self.use_sharp_yuv.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::webp::encode::EncodingConfig +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::webp::encode::EncodingConfig +{ + fn into_into_dart(self) -> crate::webp::encode::EncodingConfig { + self + } +} +impl flutter_rust_bridge::IntoDart for crate::webp::encode::Filter { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + match self { + crate::webp::encode::Filter::Simple(field0) => { + vec![0.into_dart(), field0.into_into_dart().into_dart()] + } + crate::webp::encode::Filter::Strong(field0) => { + vec![1.into_dart(), field0.into_into_dart().into_dart()] + } + } + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::webp::encode::Filter {} +impl flutter_rust_bridge::IntoIntoDart + for crate::webp::encode::Filter +{ + fn into_into_dart(self) -> crate::webp::encode::Filter { + self + } +} +impl flutter_rust_bridge::IntoDart for crate::webp::encode::FilterConfig { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + vec![ + self.strength.into_into_dart().into_dart(), + self.sharpness.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::webp::encode::FilterConfig +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::webp::encode::FilterConfig +{ + fn into_into_dart(self) -> crate::webp::encode::FilterConfig { + self + } +} +impl flutter_rust_bridge::IntoDart for crate::webp::shared::Frame { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + vec![ + self.data.into_into_dart().into_dart(), + self.width.into_into_dart().into_dart(), + self.height.into_into_dart().into_dart(), + self.timestamp.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::webp::shared::Frame {} +impl flutter_rust_bridge::IntoIntoDart for crate::webp::shared::Frame { + fn into_into_dart(self) -> crate::webp::shared::Frame { + self + } +} +impl flutter_rust_bridge::IntoDart for crate::webp::encode::Preprocessing { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + match self { + Self::SegmentSmooth => 0, + } + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::webp::encode::Preprocessing +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::webp::encode::Preprocessing +{ + fn into_into_dart(self) -> crate::webp::encode::Preprocessing { + self + } +} + +impl SseEncode for crate::webp::encode::AlphaFilter { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self as _, serializer); + } +} + +impl SseEncode for bool { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self as _).unwrap(); + } +} + +impl SseEncode for crate::webp::encode::EncodingConfig { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.method, serializer); + ::sse_encode(self.losless, serializer); + ::sse_encode(self.quality, serializer); + ::sse_encode(self.target_size, serializer); + ::sse_encode(self.target_psnr, serializer); + ::sse_encode(self.segments, serializer); + ::sse_encode(self.noise_shaping, serializer); + >::sse_encode(self.filter, serializer); + ::sse_encode(self.alpha_compression, serializer); + >::sse_encode(self.alpha_filtering, serializer); + ::sse_encode(self.alpha_quality, serializer); + ::sse_encode(self.pass, serializer); + ::sse_encode(self.show_compressed, serializer); + >::sse_encode(self.preprocessing, serializer); + ::sse_encode(self.partitions, serializer); + ::sse_encode(self.partition_limit, serializer); + ::sse_encode(self.use_sharp_yuv, serializer); + } +} + +impl SseEncode for f32 { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_f32::(self).unwrap(); + } +} + +impl SseEncode for crate::webp::encode::Filter { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + match self { + crate::webp::encode::Filter::Simple(field0) => { + ::sse_encode(0, serializer); + ::sse_encode(field0, serializer); + } + crate::webp::encode::Filter::Strong(field0) => { + ::sse_encode(1, serializer); + ::sse_encode(field0, serializer); + } + } + } +} + +impl SseEncode for crate::webp::encode::FilterConfig { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.strength, serializer); + ::sse_encode(self.sharpness, serializer); + } +} + +impl SseEncode for crate::webp::shared::Frame { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + >::sse_encode(self.data, serializer); + ::sse_encode(self.width, serializer); + ::sse_encode(self.height, serializer); + ::sse_encode(self.timestamp, serializer); + } +} + +impl SseEncode for i32 { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_i32::(self).unwrap(); + } +} + +impl SseEncode for Vec { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + +impl SseEncode for Vec { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + +impl SseEncode for Option { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for Option { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for Option { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for Option { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + +impl SseEncode for crate::webp::encode::Preprocessing { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self as _, serializer); + } +} + +impl SseEncode for u32 { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u32::(self).unwrap(); + } +} + +impl SseEncode for u8 { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + serializer.cursor.write_u8(self).unwrap(); + } +} + +impl SseEncode for () { + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} +} + +#[cfg(not(target_family = "wasm"))] +#[path = "frb_generated.io.rs"] +mod io; +#[cfg(not(target_family = "wasm"))] +pub use io::*; + +/// cbindgen:ignore +#[cfg(target_family = "wasm")] +#[path = "frb_generated.web.rs"] +mod web; +#[cfg(target_family = "wasm")] +pub use web::*; diff --git a/src/rust/src/frb_generated.web.rs b/src/rust/src/frb_generated.web.rs new file mode 100644 index 0000000..8b8d53e --- /dev/null +++ b/src/rust/src/frb_generated.web.rs @@ -0,0 +1,219 @@ +// This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ 2.0.0-dev.11. + +// Section: imports + +use super::*; +use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use flutter_rust_bridge::for_generated::transform_result_dco; +use flutter_rust_bridge::for_generated::wasm_bindgen; +use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*; +use flutter_rust_bridge::{Handler, IntoIntoDart}; + +// Section: dart2rust + +impl CstDecode> for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +where + JsValue: CstDecode, +{ + fn cst_decode(self) -> Option { + (!self.is_null() && !self.is_undefined()).then(|| self.cst_decode()) + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::encode::EncodingConfig { + let self_ = self + .dyn_into::() + .unwrap(); + assert_eq!( + self_.length(), + 17, + "Expected 17 elements, got {}", + self_.length() + ); + crate::webp::encode::EncodingConfig { + method: self_.get(0).cst_decode(), + losless: self_.get(1).cst_decode(), + quality: self_.get(2).cst_decode(), + target_size: self_.get(3).cst_decode(), + target_psnr: self_.get(4).cst_decode(), + segments: self_.get(5).cst_decode(), + noise_shaping: self_.get(6).cst_decode(), + filter: self_.get(7).cst_decode(), + alpha_compression: self_.get(8).cst_decode(), + alpha_filtering: self_.get(9).cst_decode(), + alpha_quality: self_.get(10).cst_decode(), + pass: self_.get(11).cst_decode(), + show_compressed: self_.get(12).cst_decode(), + preprocessing: self_.get(13).cst_decode(), + partitions: self_.get(14).cst_decode(), + partition_limit: self_.get(15).cst_decode(), + use_sharp_yuv: self_.get(16).cst_decode(), + } + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::encode::Filter { + let self_ = self.unchecked_into::(); + match self_.get(0).unchecked_into_f64() as _ { + 0 => crate::webp::encode::Filter::Simple(self_.get(1).cst_decode()), + 1 => crate::webp::encode::Filter::Strong(self_.get(1).cst_decode()), + _ => unreachable!(), + } + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::encode::FilterConfig { + let self_ = self + .dyn_into::() + .unwrap(); + assert_eq!( + self_.length(), + 2, + "Expected 2 elements, got {}", + self_.length() + ); + crate::webp::encode::FilterConfig { + strength: self_.get(0).cst_decode(), + sharpness: self_.get(1).cst_decode(), + } + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::shared::Frame { + let self_ = self + .dyn_into::() + .unwrap(); + assert_eq!( + self_.length(), + 4, + "Expected 4 elements, got {}", + self_.length() + ); + crate::webp::shared::Frame { + data: self_.get(0).cst_decode(), + width: self_.get(1).cst_decode(), + height: self_.get(2).cst_decode(), + timestamp: self_.get(3).cst_decode(), + } + } +} +impl CstDecode> + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> Vec { + self.dyn_into::() + .unwrap() + .iter() + .map(CstDecode::cst_decode) + .collect() + } +} +impl CstDecode> for Box<[u8]> { + fn cst_decode(self) -> Vec { + self.into_vec() + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::encode::AlphaFilter { + (self.unchecked_into_f64() as i32).cst_decode() + } +} +impl CstDecode for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> bool { + self.is_truthy() + } +} +impl CstDecode for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> f32 { + self.unchecked_into_f64() as _ + } +} +impl CstDecode for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> i32 { + self.unchecked_into_f64() as _ + } +} +impl CstDecode> for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> Vec { + self.unchecked_into::() + .to_vec() + .into() + } +} +impl CstDecode + for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue +{ + fn cst_decode(self) -> crate::webp::encode::Preprocessing { + (self.unchecked_into_f64() as i32).cst_decode() + } +} +impl CstDecode for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> u32 { + self.unchecked_into_f64() as _ + } +} +impl CstDecode for flutter_rust_bridge::for_generated::wasm_bindgen::JsValue { + fn cst_decode(self) -> u8 { + self.unchecked_into_f64() as _ + } +} + +#[wasm_bindgen] +pub fn dart_fn_deliver_output( + call_id: i32, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + FLUTTER_RUST_BRIDGE_HANDLER.dart_fn_handle_output(call_id, message) +} + +#[wasm_bindgen] +pub fn wire_calc_translucency( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: flutter_rust_bridge::for_generated::wasm_bindgen::JsValue, +) { + wire_calc_translucency_impl(port_, frames) +} + +#[wasm_bindgen] +pub fn wire_encode( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: flutter_rust_bridge::for_generated::wasm_bindgen::JsValue, + config: flutter_rust_bridge::for_generated::wasm_bindgen::JsValue, +) { + wire_encode_impl(port_, frames, config) +} + +#[wasm_bindgen] +pub fn wire_into_frames(port_: flutter_rust_bridge::for_generated::MessagePort, bytes: Box<[u8]>) { + wire_into_frames_impl(port_, bytes) +} + +#[wasm_bindgen] +pub fn wire_upscale_frames_with_padding( + port_: flutter_rust_bridge::for_generated::MessagePort, + frames: flutter_rust_bridge::for_generated::wasm_bindgen::JsValue, + width: u32, + height: u32, +) { + wire_upscale_frames_with_padding_impl(port_, frames, width, height) +} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 739dce7..78db9df 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -1,3 +1,3 @@ +mod frb_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ pub 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. */ -pub mod webp; +pub mod webp; \ No newline at end of file diff --git a/src/rust_builder/.gitignore b/src/rust_builder/.gitignore new file mode 100644 index 0000000..ac5aa98 --- /dev/null +++ b/src/rust_builder/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/src/rust_builder/README.md b/src/rust_builder/README.md new file mode 100644 index 0000000..922615f --- /dev/null +++ b/src/rust_builder/README.md @@ -0,0 +1 @@ +Please ignore this folder, which is just glue to build Rust with Flutter. \ No newline at end of file diff --git a/src/rust_builder/android/.gitignore b/src/rust_builder/android/.gitignore new file mode 100644 index 0000000..161bdcd --- /dev/null +++ b/src/rust_builder/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/src/rust_builder/android/build.gradle b/src/rust_builder/android/build.gradle new file mode 100644 index 0000000..a8517ac --- /dev/null +++ b/src/rust_builder/android/build.gradle @@ -0,0 +1,56 @@ +// The Android Gradle Plugin builds the native code with the Android NDK. + +group 'com.flutter_rust_bridge.rust_builder' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + // The Android Gradle Plugin knows how to build native code with the NDK. + classpath 'com.android.tools.build:gradle:7.3.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'com.flutter_rust_bridge.rust_builder' + } + + // Bumping the plugin compileSdkVersion requires all clients of this plugin + // to bump the version in their app. + compileSdkVersion 33 + + // Use the NDK version + // declared in /android/app/build.gradle file of the Flutter project. + // Replace it with a version number if this plugin requires a specfic NDK version. + // (e.g. ndkVersion "23.1.7779620") + ndkVersion android.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 19 + } +} + +apply from: "../cargokit/gradle/plugin.gradle" +cargokit { + manifestDir = "../../rust" + libname = "rust_lib" +} diff --git a/src/rust_builder/android/settings.gradle b/src/rust_builder/android/settings.gradle new file mode 100644 index 0000000..af87477 --- /dev/null +++ b/src/rust_builder/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'rust_builder' diff --git a/src/rust_builder/android/src/main/AndroidManifest.xml b/src/rust_builder/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5c0538a --- /dev/null +++ b/src/rust_builder/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/src/rust_builder/cargokit/.gitignore b/src/rust_builder/cargokit/.gitignore new file mode 100644 index 0000000..cf7bb86 --- /dev/null +++ b/src/rust_builder/cargokit/.gitignore @@ -0,0 +1,4 @@ +target +.dart_tool +*.iml +!pubspec.lock diff --git a/src/rust_builder/cargokit/LICENSE b/src/rust_builder/cargokit/LICENSE new file mode 100644 index 0000000..d33a5fe --- /dev/null +++ b/src/rust_builder/cargokit/LICENSE @@ -0,0 +1,42 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Copyright 2022 Matej Knopp + +================================================================================ + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ + +APACHE LICENSE, VERSION 2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/src/rust_builder/cargokit/README b/src/rust_builder/cargokit/README new file mode 100644 index 0000000..398474d --- /dev/null +++ b/src/rust_builder/cargokit/README @@ -0,0 +1,11 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +Experimental repository to provide glue for seamlessly integrating cargo build +with flutter plugins and packages. + +See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ +for a tutorial on how to use Cargokit. + +Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. + diff --git a/src/rust_builder/cargokit/build_pod.sh b/src/rust_builder/cargokit/build_pod.sh new file mode 100644 index 0000000..379b9c6 --- /dev/null +++ b/src/rust_builder/cargokit/build_pod.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -e + +BASEDIR=$(dirname "$0") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +BASEDIR=$(cd "$BASEDIR" ; pwd -P) + +# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project +NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` + +export PATH=${NEW_PATH%?} # remove trailing : + +env + +# Platform name (macosx, iphoneos, iphonesimulator) +export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME + +# Arctive architectures (arm64, armv7, x86_64), space separated. +export CARGOKIT_DARWIN_ARCHS=$ARCHS + +# Current build configuration (Debug, Release) +export CARGOKIT_CONFIGURATION=$CONFIGURATION + +# Path to directory containing Cargo.toml. +export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 + +# Temporary directory for build artifacts. +export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR + +# Output directory for final artifacts. +export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME + +# Directory to store built tool artifacts. +export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool + +# Directory inside root project. Not necessarily the top level directory of root project. +export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT + +FLUTTER_EXPORT_BUILD_ENVIRONMENT=( + "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS + "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS +) + +for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" +do + if [[ -f "$path" ]]; then + source "$path" + fi +done + +"$BASEDIR/run_build_tool.sh" build-pod "$@" + +# Make a symlink from built framework to phony file, which will be used as input to +# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate +# attribute on custom build phase) +ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" +ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" diff --git a/src/rust_builder/cargokit/build_tool/README.md b/src/rust_builder/cargokit/build_tool/README.md new file mode 100644 index 0000000..a878c27 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/README.md @@ -0,0 +1,5 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/src/rust_builder/cargokit/build_tool/analysis_options.yaml b/src/rust_builder/cargokit/build_tool/analysis_options.yaml new file mode 100644 index 0000000..0e16a8b --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/analysis_options.yaml @@ -0,0 +1,34 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +linter: + rules: + - prefer_relative_imports + - directives_ordering + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/src/rust_builder/cargokit/build_tool/bin/build_tool.dart b/src/rust_builder/cargokit/build_tool/bin/build_tool.dart new file mode 100644 index 0000000..268eb52 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/bin/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:build_tool/build_tool.dart' as build_tool; + +void main(List arguments) { + build_tool.runMain(arguments); +} diff --git a/src/rust_builder/cargokit/build_tool/lib/build_tool.dart b/src/rust_builder/cargokit/build_tool/lib/build_tool.dart new file mode 100644 index 0000000..7c1bb75 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/build_tool.dart @@ -0,0 +1,8 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'src/build_tool.dart' as build_tool; + +Future runMain(List args) async { + return build_tool.runMain(args); +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/src/rust_builder/cargokit/build_tool/lib/src/android_environment.dart new file mode 100644 index 0000000..2b0b712 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/android_environment.dart @@ -0,0 +1,195 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; +import 'dart:isolate'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; +import 'package:version/version.dart'; + +import 'target.dart'; +import 'util.dart'; + +class AndroidEnvironment { + AndroidEnvironment({ + required this.sdkPath, + required this.ndkVersion, + required this.minSdkVersion, + required this.targetTempDir, + required this.target, + }); + + static void clangLinkerWrapper(List args) { + final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; + if (clang == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); + } + final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; + if (target == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); + } + + runCommand(clang, [ + target, + ...args, + ]); + } + + /// Full path to Android SDK. + final String sdkPath; + + /// Full version of Android NDK. + final String ndkVersion; + + /// Minimum supported SDK version. + final int minSdkVersion; + + /// Target directory for build artifacts. + final String targetTempDir; + + /// Target being built. + final Target target; + + bool ndkIsInstalled() { + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); + return ndkPackageXml.existsSync(); + } + + void installNdk({ + required String javaHome, + }) { + final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; + final sdkManager = path.join( + sdkPath, + 'cmdline-tools', + 'latest', + 'bin', + 'sdkmanager$sdkManagerExtension', + ); + + log.info('Installing NDK $ndkVersion'); + runCommand(sdkManager, [ + '--install', + 'ndk;$ndkVersion', + ], environment: { + 'JAVA_HOME': javaHome, + }); + } + + Future> buildEnvironment() async { + final hostArch = Platform.isMacOS + ? "darwin-x86_64" + : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); + + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final toolchainPath = path.join( + ndkPath, + 'toolchains', + 'llvm', + 'prebuilt', + hostArch, + 'bin', + ); + + final minSdkVersion = + math.max(target.androidMinSdkVersion!, this.minSdkVersion); + + final exe = Platform.isWindows ? '.exe' : ''; + + final arKey = 'AR_${target.rust}'; + final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] + .map((e) => path.join(toolchainPath, e)) + .firstWhereOrNull((element) => File(element).existsSync()); + if (arValue == null) { + throw Exception('Failed to find ar for $target in $toolchainPath'); + } + + final targetArg = '--target=${target.rust}$minSdkVersion'; + + final ccKey = 'CC_${target.rust}'; + final ccValue = path.join(toolchainPath, 'clang$exe'); + final cfFlagsKey = 'CFLAGS_${target.rust}'; + final cFlagsValue = targetArg; + + final cxxKey = 'CXX_${target.rust}'; + final cxxValue = path.join(toolchainPath, 'clang++$exe'); + final cxxfFlagsKey = 'CXXFLAGS_${target.rust}'; + final cxxFlagsValue = targetArg; + + final linkerKey = + 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); + + final ranlibKey = 'RANLIB_${target.rust}'; + final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); + + final ndkVersionParsed = Version.parse(ndkVersion); + final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; + final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); + + final runRustTool = + Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; + + final packagePath = (await Isolate.resolvePackageUri( + Uri.parse('package:build_tool/buildtool.dart')))! + .toFilePath(); + final selfPath = path.canonicalize(path.join( + packagePath, + '..', + '..', + '..', + runRustTool, + )); + + // Make sure that run_build_tool is working properly even initially launched directly + // through dart run. + final toolTempDir = + Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; + + return { + arKey: arValue, + ccKey: ccValue, + cfFlagsKey: cFlagsValue, + cxxKey: cxxValue, + cxxfFlagsKey: cxxFlagsValue, + ranlibKey: ranlibValue, + rustFlagsKey: rustFlagsValue, + linkerKey: selfPath, + // Recognized by main() so we know when we're acting as a wrapper + '_CARGOKIT_NDK_LINK_TARGET': targetArg, + '_CARGOKIT_NDK_LINK_CLANG': ccValue, + 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, + }; + } + + // Workaround for libgcc missing in NDK23, inspired by cargo-ndk + String _libGccWorkaround(String buildDir, Version ndkVersion) { + final workaroundDir = path.join( + buildDir, + 'cargokit', + 'libgcc_workaround', + '${ndkVersion.major}', + ); + Directory(workaroundDir).createSync(recursive: true); + if (ndkVersion.major >= 23) { + File(path.join(workaroundDir, 'libgcc.a')) + .writeAsStringSync('INPUT(-lunwind)'); + } else { + // Other way around, untested, forward libgcc.a from libunwind once Rust + // gets updated for NDK23+. + File(path.join(workaroundDir, 'libunwind.a')) + .writeAsStringSync('INPUT(-lgcc)'); + } + + var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; + if (rustFlags.isNotEmpty) { + rustFlags = '$rustFlags\x1f'; + } + rustFlags = '$rustFlags-L\x1f$workaroundDir'; + return rustFlags; + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart b/src/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart new file mode 100644 index 0000000..e608cec --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/artifacts_provider.dart @@ -0,0 +1,266 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'builder.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'rustup.dart'; +import 'target.dart'; + +class Artifact { + /// File system location of the artifact. + final String path; + + /// Actual file name that the artifact should have in destination folder. + final String finalFileName; + + AritifactType get type { + if (finalFileName.endsWith('.dll') || + finalFileName.endsWith('.dll.lib') || + finalFileName.endsWith('.pdb') || + finalFileName.endsWith('.so') || + finalFileName.endsWith('.dylib')) { + return AritifactType.dylib; + } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { + return AritifactType.staticlib; + } else { + throw Exception('Unknown artifact type for $finalFileName'); + } + } + + Artifact({ + required this.path, + required this.finalFileName, + }); +} + +final _log = Logger('artifacts_provider'); + +class ArtifactProvider { + ArtifactProvider({ + required this.environment, + required this.userOptions, + }); + + final BuildEnvironment environment; + final CargokitUserOptions userOptions; + + Future>> getArtifacts(List targets) async { + final result = await _getPrecompiledArtifacts(targets); + + final pendingTargets = List.of(targets); + pendingTargets.removeWhere((element) => result.containsKey(element)); + + if (pendingTargets.isEmpty) { + return result; + } + + final rustup = Rustup(); + for (final target in targets) { + final builder = RustBuilder(target: target, environment: environment); + builder.prepare(rustup); + _log.info('Building ${environment.crateInfo.packageName} for $target'); + final targetDir = await builder.build(); + // For local build accept both static and dynamic libraries. + final artifactNames = { + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.dylib, + remote: false, + ), + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.staticlib, + remote: false, + ) + }; + final artifacts = artifactNames + .map((artifactName) => Artifact( + path: path.join(targetDir, artifactName), + finalFileName: artifactName, + )) + .where((element) => File(element.path).existsSync()) + .toList(); + result[target] = artifacts; + } + return result; + } + + Future>> _getPrecompiledArtifacts( + List targets) async { + if (userOptions.usePrecompiledBinaries == false) { + _log.info('Precompiled binaries are disabled'); + return {}; + } + if (environment.crateOptions.precompiledBinaries == null) { + _log.fine('Precompiled binaries not enabled for this crate'); + return {}; + } + + final start = Stopwatch()..start(); + final crateHash = CrateHash.compute(environment.manifestDir, + tempStorage: environment.targetTempDir); + _log.fine( + 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); + + final downloadedArtifactsDir = + path.join(environment.targetTempDir, 'precompiled', crateHash); + Directory(downloadedArtifactsDir).createSync(recursive: true); + + final res = >{}; + + for (final target in targets) { + final requiredArtifacts = getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + remote: true, + ); + final artifactsForTarget = []; + + for (final artifact in requiredArtifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final downloadedPath = path.join(downloadedArtifactsDir, fileName); + if (!File(downloadedPath).existsSync()) { + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + await _tryDownloadArtifacts( + crateHash: crateHash, + fileName: fileName, + signatureFileName: signatureFileName, + finalPath: downloadedPath, + ); + } + if (File(downloadedPath).existsSync()) { + artifactsForTarget.add(Artifact( + path: downloadedPath, + finalFileName: artifact, + )); + } else { + break; + } + } + + // Only provide complete set of artifacts. + if (artifactsForTarget.length == requiredArtifacts.length) { + _log.fine('Found precompiled artifacts for $target'); + res[target] = artifactsForTarget; + } + } + + return res; + } + + static Future _get(Uri url, {Map? headers}) async { + int attempt = 0; + const maxAttempts = 10; + while (true) { + try { + return await get(url, headers: headers); + } on SocketException catch (e) { + // Try to detect reset by peer error and retry. + if (attempt++ < maxAttempts && + (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { + _log.severe( + 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); + await Future.delayed(Duration(seconds: 1)); + continue; + } else { + rethrow; + } + } + } + } + + Future _tryDownloadArtifacts({ + required String crateHash, + required String fileName, + required String signatureFileName, + required String finalPath, + }) async { + final precompiledBinaries = environment.crateOptions.precompiledBinaries!; + final prefix = precompiledBinaries.uriPrefix; + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); + _log.fine('Downloading signature from $signatureUrl'); + final signature = await _get(signatureUrl); + if (signature.statusCode == 404) { + _log.warning( + 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); + return; + } + if (signature.statusCode != 200) { + _log.severe( + 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); + return; + } + _log.fine('Downloading binary from $url'); + final res = await _get(url); + if (res.statusCode != 200) { + _log.severe('Failed to download binary $url: status ${res.statusCode}'); + return; + } + if (verify( + precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { + File(finalPath).writeAsBytesSync(res.bodyBytes); + } else { + _log.shout('Signature verification failed! Ignoring binary.'); + } + } +} + +enum AritifactType { + staticlib, + dylib, +} + +AritifactType artifactTypeForTarget(Target target) { + if (target.darwinPlatform != null) { + return AritifactType.staticlib; + } else { + return AritifactType.dylib; + } +} + +List getArtifactNames({ + required Target target, + required String libraryName, + required bool remote, + AritifactType? aritifactType, +}) { + aritifactType ??= artifactTypeForTarget(target); + if (target.darwinArch != null) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.dylib']; + } + } else if (target.rust.contains('-windows-')) { + if (aritifactType == AritifactType.staticlib) { + return ['$libraryName.lib']; + } else { + return [ + '$libraryName.dll', + '$libraryName.dll.lib', + if (!remote) '$libraryName.pdb' + ]; + } + } else if (target.rust.contains('-linux-')) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.so']; + } + } else { + throw Exception("Unsupported target: ${target.rust}"); + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart b/src/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart new file mode 100644 index 0000000..6f3b2a4 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/build_cmake.dart @@ -0,0 +1,40 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +class BuildCMake { + final CargokitUserOptions userOptions; + + BuildCMake({required this.userOptions}); + + Future build() async { + final targetPlatform = Environment.targetPlatform; + final target = Target.forFlutterName(Environment.targetPlatform); + if (target == null) { + throw Exception("Unknown target platform: $targetPlatform"); + } + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts([target]); + + final libs = artifacts[target]!; + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path) + .copySync(path.join(Environment.outputDir, lib.finalFileName)); + } + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart b/src/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart new file mode 100644 index 0000000..7e61fcb --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/build_gradle.dart @@ -0,0 +1,49 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +final log = Logger('build_gradle'); + +class BuildGradle { + BuildGradle({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.targetPlatforms.map((arch) { + final target = Target.forFlutterName(arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: true); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + for (final target in targets) { + final libs = artifacts[target]!; + final outputDir = path.join(Environment.outputDir, target.android!); + Directory(outputDir).createSync(recursive: true); + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); + } + } + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/build_pod.dart b/src/rust_builder/cargokit/build_tool/lib/src/build_pod.dart new file mode 100644 index 0000000..8a9c0db --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/build_pod.dart @@ -0,0 +1,89 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; +import 'util.dart'; + +class BuildPod { + BuildPod({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.darwinArchs.map((arch) { + final target = Target.forDarwin( + platformName: Environment.darwinPlatformName, darwinAarch: arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + void performLipo(String targetFile, Iterable sourceFiles) { + runCommand("lipo", [ + '-create', + ...sourceFiles, + '-output', + targetFile, + ]); + } + + final outputDir = Environment.outputDir; + + Directory(outputDir).createSync(recursive: true); + + final staticLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.staticlib) + .toList(); + final dynamicLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.dylib) + .toList(); + + final libName = environment.crateInfo.packageName; + + // If there is static lib, use it and link it with pod + if (staticLibs.isNotEmpty) { + final finalTargetFile = path.join(outputDir, "lib$libName.a"); + performLipo(finalTargetFile, staticLibs.map((e) => e.path)); + } else { + // Otherwise try to replace bundle dylib with our dylib + final bundlePaths = [ + '$libName.framework/Versions/A/$libName', + '$libName.framework/$libName', + ]; + + for (final bundlePath in bundlePaths) { + final targetFile = path.join(outputDir, bundlePath); + if (File(targetFile).existsSync()) { + performLipo(targetFile, dynamicLibs.map((e) => e.path)); + + // Replace absolute id with @rpath one so that it works properly + // when moved to Frameworks. + runCommand("install_name_tool", [ + '-id', + '@rpath/$bundlePath', + targetFile, + ]); + return; + } + } + throw Exception('Unable to find bundle for dynamic library'); + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/build_tool.dart b/src/rust_builder/cargokit/build_tool/lib/src/build_tool.dart new file mode 100644 index 0000000..c8f3698 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/build_tool.dart @@ -0,0 +1,271 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; + +import 'android_environment.dart'; +import 'build_cmake.dart'; +import 'build_gradle.dart'; +import 'build_pod.dart'; +import 'logging.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; +import 'util.dart'; +import 'verify_binaries.dart'; + +final log = Logger('build_tool'); + +abstract class BuildCommand extends Command { + Future runBuildCommand(CargokitUserOptions options); + + @override + Future run() async { + final options = CargokitUserOptions.load(); + + if (options.verboseLogging || + Platform.environment['CARGOKIT_VERBOSE'] == '1') { + enableVerboseLogging(); + } + + await runBuildCommand(options); + } +} + +class BuildPodCommand extends BuildCommand { + @override + final name = 'build-pod'; + + @override + final description = 'Build cocoa pod library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildPod(userOptions: options); + await build.build(); + } +} + +class BuildGradleCommand extends BuildCommand { + @override + final name = 'build-gradle'; + + @override + final description = 'Build android library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildGradle(userOptions: options); + await build.build(); + } +} + +class BuildCMakeCommand extends BuildCommand { + @override + final name = 'build-cmake'; + + @override + final description = 'Build CMake library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildCMake(userOptions: options); + await build.build(); + } +} + +class GenKeyCommand extends Command { + @override + final name = 'gen-key'; + + @override + final description = 'Generate key pair for signing precompiled binaries'; + + @override + void run() { + final kp = generateKey(); + final private = HEX.encode(kp.privateKey.bytes); + final public = HEX.encode(kp.publicKey.bytes); + print("Private Key: $private"); + print("Public Key: $public"); + } +} + +class PrecompileBinariesCommand extends Command { + PrecompileBinariesCommand() { + argParser + ..addOption( + 'repository', + mandatory: true, + help: 'Github repository slug in format owner/name', + ) + ..addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ) + ..addMultiOption('target', + help: 'Rust target triple of artifact to build.\n' + 'Can be specified multiple times or omitted in which case\n' + 'all targets for current platform will be built.') + ..addOption( + 'android-sdk-location', + help: 'Location of Android SDK (if available)', + ) + ..addOption( + 'android-ndk-version', + help: 'Android NDK version (if available)', + ) + ..addOption( + 'android-min-sdk-version', + help: 'Android minimum rquired version (if available)', + ) + ..addOption( + 'temp-dir', + help: 'Directory to store temporary build artifacts', + ) + ..addFlag( + "verbose", + abbr: "v", + defaultsTo: false, + help: "Enable verbose logging", + ); + } + + @override + final name = 'precompile-binaries'; + + @override + final description = 'Prebuild and upload binaries\n' + 'Private key must be passed through PRIVATE_KEY environment variable. ' + 'Use gen_key through generate priave key.\n' + 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; + + @override + Future run() async { + final verbose = argResults!['verbose'] as bool; + if (verbose) { + enableVerboseLogging(); + } + + final privateKeyString = Platform.environment['PRIVATE_KEY']; + if (privateKeyString == null) { + throw ArgumentError('Missing PRIVATE_KEY environment variable'); + } + final githubToken = Platform.environment['GITHUB_TOKEN']; + if (githubToken == null) { + throw ArgumentError('Missing GITHUB_TOKEN environment variable'); + } + final privateKey = HEX.decode(privateKeyString); + if (privateKey.length != 64) { + throw ArgumentError('Private key must be 64 bytes long'); + } + final manifestDir = argResults!['manifest-dir'] as String; + if (!Directory(manifestDir).existsSync()) { + throw ArgumentError('Manifest directory does not exist: $manifestDir'); + } + String? androidMinSdkVersionString = + argResults!['android-min-sdk-version'] as String?; + int? androidMinSdkVersion; + if (androidMinSdkVersionString != null) { + androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); + if (androidMinSdkVersion == null) { + throw ArgumentError( + 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); + } + } + final targetStrigns = argResults!['target'] as List; + final targets = targetStrigns.map((target) { + final res = Target.forRustTriple(target); + if (res == null) { + throw ArgumentError('Invalid target: $target'); + } + return res; + }).toList(growable: false); + final precompileBinaries = PrecompileBinaries( + privateKey: PrivateKey(privateKey), + githubToken: githubToken, + manifestDir: manifestDir, + repositorySlug: RepositorySlug.full(argResults!['repository'] as String), + targets: targets, + androidSdkLocation: argResults!['android-sdk-location'] as String?, + androidNdkVersion: argResults!['android-ndk-version'] as String?, + androidMinSdkVersion: androidMinSdkVersion, + tempDir: argResults!['temp-dir'] as String?, + ); + + await precompileBinaries.run(); + } +} + +class VerifyBinariesCommand extends Command { + VerifyBinariesCommand() { + argParser.addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ); + } + + @override + final name = "verify-binaries"; + + @override + final description = 'Verifies published binaries\n' + 'Checks whether there is a binary published for each targets\n' + 'and checks the signature.'; + + @override + Future run() async { + final manifestDir = argResults!['manifest-dir'] as String; + final verifyBinaries = VerifyBinaries( + manifestDir: manifestDir, + ); + await verifyBinaries.run(); + } +} + +Future runMain(List args) async { + try { + // Init logging before options are loaded + initLogging(); + + if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { + return AndroidEnvironment.clangLinkerWrapper(args); + } + + final runner = CommandRunner('build_tool', 'Cargokit built_tool') + ..addCommand(BuildPodCommand()) + ..addCommand(BuildGradleCommand()) + ..addCommand(BuildCMakeCommand()) + ..addCommand(GenKeyCommand()) + ..addCommand(PrecompileBinariesCommand()) + ..addCommand(VerifyBinariesCommand()); + + await runner.run(args); + } on ArgumentError catch (e) { + stderr.writeln(e.toString()); + exit(1); + } catch (e, s) { + log.severe(kDoubleSeparator); + log.severe('Cargokit BuildTool failed with error:'); + log.severe(kSeparator); + log.severe(e); + // This tells user to install Rust, there's no need to pollute the log with + // stack trace. + if (e is! RustupNotFoundException) { + log.severe(kSeparator); + log.severe(s); + log.severe(kSeparator); + log.severe('BuildTool arguments: $args'); + } + log.severe(kDoubleSeparator); + exit(1); + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/builder.dart b/src/rust_builder/cargokit/build_tool/lib/src/builder.dart new file mode 100644 index 0000000..84c46e4 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/builder.dart @@ -0,0 +1,198 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'package:collection/collection.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'android_environment.dart'; +import 'cargo.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; +import 'util.dart'; + +final _log = Logger('builder'); + +enum BuildConfiguration { + debug, + release, + profile, +} + +extension on BuildConfiguration { + bool get isDebug => this == BuildConfiguration.debug; + String get rustName => switch (this) { + BuildConfiguration.debug => 'debug', + BuildConfiguration.release => 'release', + BuildConfiguration.profile => 'release', + }; +} + +class BuildException implements Exception { + final String message; + + BuildException(this.message); + + @override + String toString() { + return 'BuildException: $message'; + } +} + +class BuildEnvironment { + final BuildConfiguration configuration; + final CargokitCrateOptions crateOptions; + final String targetTempDir; + final String manifestDir; + final CrateInfo crateInfo; + + final bool isAndroid; + final String? androidSdkPath; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? javaHome; + + BuildEnvironment({ + required this.configuration, + required this.crateOptions, + required this.targetTempDir, + required this.manifestDir, + required this.crateInfo, + required this.isAndroid, + this.androidSdkPath, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.javaHome, + }); + + static BuildConfiguration parseBuildConfiguration(String value) { + // XCode configuration adds the flavor to configuration name. + final firstSegment = value.split('-').first; + final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( + (e) => e.name == firstSegment, + ); + if (buildConfiguration == null) { + _log.warning('Unknown build configuraiton $value, will assume release'); + return BuildConfiguration.release; + } + return buildConfiguration; + } + + static BuildEnvironment fromEnvironment({ + required bool isAndroid, + }) { + final buildConfiguration = + parseBuildConfiguration(Environment.configuration); + final manifestDir = Environment.manifestDir; + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + final crateInfo = CrateInfo.load(manifestDir); + return BuildEnvironment( + configuration: buildConfiguration, + crateOptions: crateOptions, + targetTempDir: Environment.targetTempDir, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: isAndroid, + androidSdkPath: isAndroid ? Environment.sdkPath : null, + androidNdkVersion: isAndroid ? Environment.ndkVersion : null, + androidMinSdkVersion: + isAndroid ? int.parse(Environment.minSdkVersion) : null, + javaHome: isAndroid ? Environment.javaHome : null, + ); + } +} + +class RustBuilder { + final Target target; + final BuildEnvironment environment; + + RustBuilder({ + required this.target, + required this.environment, + }); + + void prepare( + Rustup rustup, + ) { + final toolchain = _toolchain; + if (rustup.installedTargets(toolchain) == null) { + rustup.installToolchain(toolchain); + } + if (toolchain == 'nightly') { + rustup.installRustSrcForNightly(); + } + if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { + rustup.installTarget(target.rust, toolchain: toolchain); + } + } + + CargoBuildOptions? get _buildOptions => + environment.crateOptions.cargo[environment.configuration]; + + String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; + + /// Returns the path of directory containing build artifacts. + Future build() async { + final extraArgs = _buildOptions?.flags ?? []; + final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); + runCommand( + 'rustup', + [ + 'run', + _toolchain, + 'cargo', + 'build', + ...extraArgs, + '--manifest-path', + manifestPath, + '-p', + environment.crateInfo.packageName, + if (!environment.configuration.isDebug) '--release', + '--target', + target.rust, + '--target-dir', + environment.targetTempDir, + ], + environment: await _buildEnvironment(), + ); + return path.join( + environment.targetTempDir, + target.rust, + environment.configuration.rustName, + ); + } + + Future> _buildEnvironment() async { + if (target.android == null) { + return {}; + } else { + final sdkPath = environment.androidSdkPath; + final ndkVersion = environment.androidNdkVersion; + final minSdkVersion = environment.androidMinSdkVersion; + if (sdkPath == null) { + throw BuildException('androidSdkPath is not set'); + } + if (ndkVersion == null) { + throw BuildException('androidNdkVersion is not set'); + } + if (minSdkVersion == null) { + throw BuildException('androidMinSdkVersion is not set'); + } + final env = AndroidEnvironment( + sdkPath: sdkPath, + ndkVersion: ndkVersion, + minSdkVersion: minSdkVersion, + targetTempDir: environment.targetTempDir, + target: target, + ); + if (!env.ndkIsInstalled() && environment.javaHome != null) { + env.installNdk(javaHome: environment.javaHome!); + } + return env.buildEnvironment(); + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/cargo.dart b/src/rust_builder/cargokit/build_tool/lib/src/cargo.dart new file mode 100644 index 0000000..0d8958f --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/cargo.dart @@ -0,0 +1,48 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:toml/toml.dart'; + +class ManifestException { + ManifestException(this.message, {required this.fileName}); + + final String? fileName; + final String message; + + @override + String toString() { + if (fileName != null) { + return 'Failed to parse package manifest at $fileName: $message'; + } else { + return 'Failed to parse package manifest: $message'; + } + } +} + +class CrateInfo { + CrateInfo({required this.packageName}); + + final String packageName; + + static CrateInfo parseManifest(String manifest, {final String? fileName}) { + final toml = TomlDocument.parse(manifest); + final package = toml.toMap()['package']; + if (package == null) { + throw ManifestException('Missing package section', fileName: fileName); + } + final name = package['name']; + if (name == null) { + throw ManifestException('Missing package name', fileName: fileName); + } + return CrateInfo(packageName: name); + } + + static CrateInfo load(String manifestDir) { + final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); + final manifest = manifestFile.readAsStringSync(); + return parseManifest(manifest, fileName: manifestFile.path); + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart b/src/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart new file mode 100644 index 0000000..0c4d88d --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/crate_hash.dart @@ -0,0 +1,124 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart' as path; + +class CrateHash { + /// Computes a hash uniquely identifying crate content. This takes into account + /// content all all .rs files inside the src directory, as well as Cargo.toml, + /// Cargo.lock, build.rs and cargokit.yaml. + /// + /// If [tempStorage] is provided, computed hash is stored in a file in that directory + /// and reused on subsequent calls if the crate content hasn't changed. + static String compute(String manifestDir, {String? tempStorage}) { + return CrateHash._( + manifestDir: manifestDir, + tempStorage: tempStorage, + )._compute(); + } + + CrateHash._({ + required this.manifestDir, + required this.tempStorage, + }); + + String _compute() { + final files = getFiles(); + final tempStorage = this.tempStorage; + if (tempStorage != null) { + final quickHash = _computeQuickHash(files); + final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); + quickHashFolder.createSync(recursive: true); + final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); + if (quickHashFile.existsSync()) { + return quickHashFile.readAsStringSync(); + } + final hash = _computeHash(files); + quickHashFile.writeAsStringSync(hash); + return hash; + } else { + return _computeHash(files); + } + } + + /// Computes a quick hash based on files stat (without reading contents). This + /// is used to cache the real hash, which is slower to compute since it involves + /// reading every single file. + String _computeQuickHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + final data = ByteData(8); + for (final file in files) { + input.add(utf8.encode(file.path)); + final stat = file.statSync(); + data.setUint64(0, stat.size); + input.add(data.buffer.asUint8List()); + data.setUint64(0, stat.modified.millisecondsSinceEpoch); + input.add(data.buffer.asUint8List()); + } + + input.close(); + return base64Url.encode(output.events.single.bytes); + } + + String _computeHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + void addTextFile(File file) { + // text Files are hashed by lines in case we're dealing with github checkout + // that auto-converts line endings. + final splitter = LineSplitter(); + if (file.existsSync()) { + final data = file.readAsStringSync(); + final lines = splitter.convert(data); + for (final line in lines) { + input.add(utf8.encode(line)); + } + } + } + + for (final file in files) { + addTextFile(file); + } + + input.close(); + final res = output.events.single; + + // Truncate to 128bits. + final hash = res.bytes.sublist(0, 16); + return hex.encode(hash); + } + + List getFiles() { + final src = Directory(path.join(manifestDir, 'src')); + final files = src + .listSync(recursive: true, followLinks: false) + .whereType() + .toList(); + files.sortBy((element) => element.path); + void addFile(String relative) { + final file = File(path.join(manifestDir, relative)); + if (file.existsSync()) { + files.add(file); + } + } + + addFile('Cargo.toml'); + addFile('Cargo.lock'); + addFile('build.rs'); + addFile('cargokit.yaml'); + return files; + } + + final String manifestDir; + final String? tempStorage; +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/environment.dart b/src/rust_builder/cargokit/build_tool/lib/src/environment.dart new file mode 100644 index 0000000..996483a --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/environment.dart @@ -0,0 +1,68 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +extension on String { + String resolveSymlink() => File(this).resolveSymbolicLinksSync(); +} + +class Environment { + /// Current build configuration (debug or release). + static String get configuration => + _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); + + static bool get isDebug => configuration == 'debug'; + static bool get isRelease => configuration == 'release'; + + /// Temporary directory where Rust build artifacts are placed. + static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); + + /// Final output directory where the build artifacts are placed. + static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); + + /// Path to the crate manifest (containing Cargo.toml). + static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); + + /// Directory inside root project. Not necessarily root folder. Symlinks are + /// not resolved on purpose. + static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); + + // Pod + + /// Platform name (macosx, iphoneos, iphonesimulator). + static String get darwinPlatformName => + _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); + + /// List of architectures to build for (arm64, armv7, x86_64). + static List get darwinArchs => + _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); + + // Gradle + static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); + static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); + static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); + static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); + static List get targetPlatforms => + _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); + + // CMAKE + static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); + + static String _getEnv(String key) { + final res = Platform.environment[key]; + if (res == null) { + throw Exception("Missing environment variable $key"); + } + return res; + } + + static String _getEnvPath(String key) { + final res = _getEnv(key); + if (Directory(res).existsSync()) { + return res.resolveSymlink(); + } else { + return res; + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/logging.dart b/src/rust_builder/cargokit/build_tool/lib/src/logging.dart new file mode 100644 index 0000000..5edd4fd --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/logging.dart @@ -0,0 +1,52 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:logging/logging.dart'; + +const String kSeparator = "--"; +const String kDoubleSeparator = "=="; + +bool _lastMessageWasSeparator = false; + +void _log(LogRecord rec) { + final prefix = '${rec.level.name}: '; + final out = rec.level == Level.SEVERE ? stderr : stdout; + if (rec.message == kSeparator) { + if (!_lastMessageWasSeparator) { + out.write(prefix); + out.writeln('-' * 80); + _lastMessageWasSeparator = true; + } + return; + } else if (rec.message == kDoubleSeparator) { + out.write(prefix); + out.writeln('=' * 80); + _lastMessageWasSeparator = true; + return; + } + out.write(prefix); + out.writeln(rec.message); + _lastMessageWasSeparator = false; +} + +void initLogging() { + Logger.root.level = Level.INFO; + Logger.root.onRecord.listen((LogRecord rec) { + final lines = rec.message.split('\n'); + for (final line in lines) { + if (line.isNotEmpty || lines.length == 1 || line != lines.last) { + _log(LogRecord( + rec.level, + line, + rec.loggerName, + )); + } + } + }); +} + +void enableVerboseLogging() { + Logger.root.level = Level.ALL; +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/options.dart b/src/rust_builder/cargokit/build_tool/lib/src/options.dart new file mode 100644 index 0000000..22aef1d --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/options.dart @@ -0,0 +1,309 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:source_span/source_span.dart'; +import 'package:yaml/yaml.dart'; + +import 'builder.dart'; +import 'environment.dart'; +import 'rustup.dart'; + +final _log = Logger('options'); + +/// A class for exceptions that have source span information attached. +class SourceSpanException implements Exception { + // This is a getter so that subclasses can override it. + /// A message describing the exception. + String get message => _message; + final String _message; + + // This is a getter so that subclasses can override it. + /// The span associated with this exception. + /// + /// This may be `null` if the source location can't be determined. + SourceSpan? get span => _span; + final SourceSpan? _span; + + SourceSpanException(this._message, this._span); + + /// Returns a string representation of `this`. + /// + /// [color] may either be a [String], a [bool], or `null`. If it's a string, + /// it indicates an ANSI terminal color escape that should be used to + /// highlight the span's text. If it's `true`, it indicates that the text + /// should be highlighted using the default color. If it's `false` or `null`, + /// it indicates that the text shouldn't be highlighted. + @override + String toString({Object? color}) { + if (span == null) return message; + return 'Error on ${span!.message(message, color: color)}'; + } +} + +enum Toolchain { + stable, + beta, + nightly, +} + +class CargoBuildOptions { + final Toolchain toolchain; + final List flags; + + CargoBuildOptions({ + required this.toolchain, + required this.flags, + }); + + static Toolchain _toolchainFromNode(YamlNode node) { + if (node case YamlScalar(value: String name)) { + final toolchain = + Toolchain.values.firstWhereOrNull((element) => element.name == name); + if (toolchain != null) { + return toolchain; + } + } + throw SourceSpanException( + 'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.', + node.span); + } + + static CargoBuildOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + Toolchain toolchain = Toolchain.stable; + List flags = []; + for (final MapEntry(:key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: 'toolchain')) { + toolchain = _toolchainFromNode(value); + } else if (key case YamlScalar(value: 'extra_flags')) { + if (value case YamlList(nodes: List list)) { + if (list.every((element) { + if (element case YamlScalar(value: String _)) { + return true; + } + return false; + })) { + flags = list.map((e) => e.value as String).toList(); + continue; + } + } + throw SourceSpanException( + 'Extra flags must be a list of strings', value.span); + } else { + throw SourceSpanException( + 'Unknown cargo option type. Must be "toolchain" or "extra_flags".', + key.span); + } + } + return CargoBuildOptions(toolchain: toolchain, flags: flags); + } +} + +extension on YamlMap { + /// Map that extracts keys so that we can do map case check on them. + Map get valueMap => + nodes.map((key, value) => MapEntry(key.value, value)); +} + +class PrecompiledBinaries { + final String uriPrefix; + final PublicKey publicKey; + + PrecompiledBinaries({ + required this.uriPrefix, + required this.publicKey, + }); + + static PublicKey _publicKeyFromHex(String key, SourceSpan? span) { + final bytes = HEX.decode(key); + if (bytes.length != 32) { + throw SourceSpanException( + 'Invalid public key. Must be 32 bytes long.', span); + } + return PublicKey(bytes); + } + + static PrecompiledBinaries parse(YamlNode node) { + if (node case YamlMap(valueMap: Map map)) { + if (map + case { + 'url_prefix': YamlNode urlPrefixNode, + 'public_key': YamlNode publicKeyNode, + }) { + final urlPrefix = switch (urlPrefixNode) { + YamlScalar(value: String urlPrefix) => urlPrefix, + _ => throw SourceSpanException( + 'Invalid URL prefix value.', urlPrefixNode.span), + }; + final publicKey = switch (publicKeyNode) { + YamlScalar(value: String publicKey) => + _publicKeyFromHex(publicKey, publicKeyNode.span), + _ => throw SourceSpanException( + 'Invalid public key value.', publicKeyNode.span), + }; + return PrecompiledBinaries( + uriPrefix: urlPrefix, + publicKey: publicKey, + ); + } + } + throw SourceSpanException( + 'Invalid precompiled binaries value. ' + 'Expected Map with "url_prefix" and "public_key".', + node.span); + } +} + +/// Cargokit options specified for Rust crate. +class CargokitCrateOptions { + CargokitCrateOptions({ + this.cargo = const {}, + this.precompiledBinaries, + }); + + final Map cargo; + final PrecompiledBinaries? precompiledBinaries; + + static CargokitCrateOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + final options = {}; + PrecompiledBinaries? precompiledBinaries; + + for (final entry in node.nodes.entries) { + if (entry + case MapEntry( + key: YamlScalar(value: 'cargo'), + value: YamlNode node, + )) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: String name)) { + final configuration = BuildConfiguration.values + .firstWhereOrNull((element) => element.name == name); + if (configuration != null) { + options[configuration] = CargoBuildOptions.parse(value); + continue; + } + } + throw SourceSpanException( + 'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.', + key.span); + } + } else if (entry.key case YamlScalar(value: 'precompiled_binaries')) { + precompiledBinaries = PrecompiledBinaries.parse(entry.value); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".', + entry.key.span); + } + } + return CargokitCrateOptions( + cargo: options, + precompiledBinaries: precompiledBinaries, + ); + } + + static CargokitCrateOptions load({ + required String manifestDir, + }) { + final uri = Uri.file(path.join(manifestDir, "cargokit.yaml")); + final file = File.fromUri(uri); + if (file.existsSync()) { + final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri); + return parse(contents); + } else { + return CargokitCrateOptions(); + } + } +} + +class CargokitUserOptions { + // When Rustup is installed always build locally unless user opts into + // using precompiled binaries. + static bool defaultUsePrecompiledBinaries() { + return Rustup.executablePath() == null; + } + + CargokitUserOptions({ + required this.usePrecompiledBinaries, + required this.verboseLogging, + }); + + CargokitUserOptions._() + : usePrecompiledBinaries = defaultUsePrecompiledBinaries(), + verboseLogging = false; + + static CargokitUserOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + bool usePrecompiledBinaries = defaultUsePrecompiledBinaries(); + bool verboseLogging = false; + + for (final entry in node.nodes.entries) { + if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) { + if (entry.value case YamlScalar(value: bool value)) { + usePrecompiledBinaries = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "use_precompiled_binaries". Must be a boolean.', + entry.value.span); + } else if (entry.key case YamlScalar(value: 'verbose_logging')) { + if (entry.value case YamlScalar(value: bool value)) { + verboseLogging = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "verbose_logging". Must be a boolean.', + entry.value.span); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".', + entry.key.span); + } + } + return CargokitUserOptions( + usePrecompiledBinaries: usePrecompiledBinaries, + verboseLogging: verboseLogging, + ); + } + + static CargokitUserOptions load() { + String fileName = "cargokit_options.yaml"; + var userProjectDir = Directory(Environment.rootProjectDir); + + while (userProjectDir.parent.path != userProjectDir.path) { + final configFile = File(path.join(userProjectDir.path, fileName)); + if (configFile.existsSync()) { + final contents = loadYamlNode( + configFile.readAsStringSync(), + sourceUrl: configFile.uri, + ); + final res = parse(contents); + if (res.verboseLogging) { + _log.info('Found user options file at ${configFile.path}'); + } + return res; + } + userProjectDir = userProjectDir.parent; + } + return CargokitUserOptions._(); + } + + final bool usePrecompiledBinaries; + final bool verboseLogging; +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart b/src/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart new file mode 100644 index 0000000..c27f419 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/precompile_binaries.dart @@ -0,0 +1,202 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; + +final _log = Logger('precompile_binaries'); + +class PrecompileBinaries { + PrecompileBinaries({ + required this.privateKey, + required this.githubToken, + required this.repositorySlug, + required this.manifestDir, + required this.targets, + this.androidSdkLocation, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.tempDir, + }); + + final PrivateKey privateKey; + final String githubToken; + final RepositorySlug repositorySlug; + final String manifestDir; + final List targets; + final String? androidSdkLocation; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? tempDir; + + static String fileName(Target target, String name) { + return '${target.rust}_$name'; + } + + static String signatureFileName(Target target, String name) { + return '${target.rust}_$name.sig'; + } + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final targets = List.of(this.targets); + if (targets.isEmpty) { + targets.addAll([ + ...Target.buildableTargets(), + if (androidSdkLocation != null) ...Target.androidTargets(), + ]); + } + + _log.info('Precompiling binaries for $targets'); + + final hash = CrateHash.compute(manifestDir); + _log.info('Computed crate hash: $hash'); + + final String tagName = 'precompiled_$hash'; + + final github = GitHub(auth: Authentication.withToken(githubToken)); + final repo = github.repositories; + final release = await _getOrCreateRelease( + repo: repo, + tagName: tagName, + packageName: crateInfo.packageName, + hash: hash, + ); + + final tempDir = this.tempDir != null + ? Directory(this.tempDir!) + : Directory.systemTemp.createTempSync('precompiled_'); + + tempDir.createSync(recursive: true); + + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + + final buildEnvironment = BuildEnvironment( + configuration: BuildConfiguration.release, + crateOptions: crateOptions, + targetTempDir: tempDir.path, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: androidSdkLocation != null, + androidSdkPath: androidSdkLocation, + androidNdkVersion: androidNdkVersion, + androidMinSdkVersion: androidMinSdkVersion, + ); + + final rustup = Rustup(); + + for (final target in targets) { + final artifactNames = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + if (artifactNames.every((name) { + final fileName = PrecompileBinaries.fileName(target, name); + return (release.assets ?? []).any((e) => e.name == fileName); + })) { + _log.info("All artifacts for $target already exist - skipping"); + continue; + } + + _log.info('Building for $target'); + + final builder = + RustBuilder(target: target, environment: buildEnvironment); + builder.prepare(rustup); + final res = await builder.build(); + + final assets = []; + for (final name in artifactNames) { + final file = File(path.join(res, name)); + if (!file.existsSync()) { + throw Exception('Missing artifact: ${file.path}'); + } + + final data = file.readAsBytesSync(); + final create = CreateReleaseAsset( + name: PrecompileBinaries.fileName(target, name), + contentType: "application/octet-stream", + assetData: data, + ); + final signature = sign(privateKey, data); + final signatureCreate = CreateReleaseAsset( + name: signatureFileName(target, name), + contentType: "application/octet-stream", + assetData: signature, + ); + bool verified = verify(public(privateKey), data, signature); + if (!verified) { + throw Exception('Signature verification failed'); + } + assets.add(create); + assets.add(signatureCreate); + } + _log.info('Uploading assets: ${assets.map((e) => e.name)}'); + for (final asset in assets) { + // This seems to be failing on CI so do it one by one + int retryCount = 0; + while (true) { + try { + await repo.uploadReleaseAssets(release, [asset]); + break; + } on Exception catch (e) { + if (retryCount == 10) { + rethrow; + } + ++retryCount; + _log.shout( + 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); + await Future.delayed(Duration(seconds: 2)); + } + } + } + } + + _log.info('Cleaning up'); + tempDir.deleteSync(recursive: true); + } + + Future _getOrCreateRelease({ + required RepositoriesService repo, + required String tagName, + required String packageName, + required String hash, + }) async { + Release release; + try { + _log.info('Fetching release $tagName'); + release = await repo.getReleaseByTagName(repositorySlug, tagName); + } on ReleaseNotFound { + _log.info('Release not found - creating release $tagName'); + release = await repo.createRelease( + repositorySlug, + CreateRelease.from( + tagName: tagName, + name: 'Precompiled binaries ${hash.substring(0, 8)}', + targetCommitish: null, + isDraft: false, + isPrerelease: false, + body: 'Precompiled binaries for crate $packageName, ' + 'crate hash $hash.', + )); + } + return release; + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/rustup.dart b/src/rust_builder/cargokit/build_tool/lib/src/rustup.dart new file mode 100644 index 0000000..77a66b1 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/rustup.dart @@ -0,0 +1,132 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; + +import 'util.dart'; + +class _Toolchain { + _Toolchain( + this.name, + this.targets, + ); + + final String name; + final List targets; +} + +class Rustup { + List? installedTargets(String toolchain) { + final targets = _installedTargets(toolchain); + return targets != null ? List.unmodifiable(targets) : null; + } + + void installToolchain(String toolchain) { + log.info("Installing Rust toolchain: $toolchain"); + runCommand("rustup", ['toolchain', 'install', toolchain]); + _installedToolchains + .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); + } + + void installTarget( + String target, { + required String toolchain, + }) { + log.info("Installing Rust target: $target"); + runCommand("rustup", [ + 'target', + 'add', + '--toolchain', + toolchain, + target, + ]); + _installedTargets(toolchain)?.add(target); + } + + final List<_Toolchain> _installedToolchains; + + Rustup() : _installedToolchains = _getInstalledToolchains(); + + List? _installedTargets(String toolchain) => _installedToolchains + .firstWhereOrNull( + (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) + ?.targets; + + static List<_Toolchain> _getInstalledToolchains() { + String extractToolchainName(String line) { + // ignore (default) after toolchain name + final parts = line.split(' '); + return parts[0]; + } + + final res = runCommand("rustup", ['toolchain', 'list']); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty) + .map(extractToolchainName) + .toList(growable: true); + + return lines + .map( + (name) => _Toolchain( + name, + _getInstalledTargets(name), + ), + ) + .toList(growable: true); + } + + static List _getInstalledTargets(String toolchain) { + final res = runCommand("rustup", [ + 'target', + 'list', + '--toolchain', + toolchain, + '--installed', + ]); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty) + .toList(growable: true); + return lines; + } + + bool _didInstallRustSrcForNightly = false; + + void installRustSrcForNightly() { + if (_didInstallRustSrcForNightly) { + return; + } + // Useful for -Z build-std + runCommand( + "rustup", + ['component', 'add', 'rust-src', '--toolchain', 'nightly'], + ); + _didInstallRustSrcForNightly = true; + } + + static String? executablePath() { + final envPath = Platform.environment['PATH']; + final envPathSeparator = Platform.isWindows ? ';' : ':'; + final home = Platform.isWindows + ? Platform.environment['USERPROFILE'] + : Platform.environment['HOME']; + final paths = [ + if (home != null) path.join(home, '.cargo', 'bin'), + if (envPath != null) ...envPath.split(envPathSeparator), + ]; + for (final p in paths) { + final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; + final rustupPath = path.join(p, rustup); + if (File(rustupPath).existsSync()) { + return rustupPath; + } + } + return null; + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/target.dart b/src/rust_builder/cargokit/build_tool/lib/src/target.dart new file mode 100644 index 0000000..6fbc58b --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/target.dart @@ -0,0 +1,140 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:collection/collection.dart'; + +import 'util.dart'; + +class Target { + Target({ + required this.rust, + this.flutter, + this.android, + this.androidMinSdkVersion, + this.darwinPlatform, + this.darwinArch, + }); + + static final all = [ + Target( + rust: 'armv7-linux-androideabi', + flutter: 'android-arm', + android: 'armeabi-v7a', + androidMinSdkVersion: 16, + ), + Target( + rust: 'aarch64-linux-android', + flutter: 'android-arm64', + android: 'arm64-v8a', + androidMinSdkVersion: 21, + ), + Target( + rust: 'i686-linux-android', + flutter: 'android-x86', + android: 'x86', + androidMinSdkVersion: 16, + ), + Target( + rust: 'x86_64-linux-android', + flutter: 'android-x64', + android: 'x86_64', + androidMinSdkVersion: 21, + ), + Target( + rust: 'x86_64-pc-windows-msvc', + flutter: 'windows-x64', + ), + Target( + rust: 'x86_64-unknown-linux-gnu', + flutter: 'linux-x64', + ), + Target( + rust: 'aarch64-unknown-linux-gnu', + flutter: 'linux-arm64', + ), + Target( + rust: 'x86_64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'x86_64', + ), + Target( + rust: 'aarch64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios', + darwinPlatform: 'iphoneos', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios-sim', + darwinPlatform: 'iphonesimulator', + darwinArch: 'arm64', + ), + Target( + rust: 'x86_64-apple-ios', + darwinPlatform: 'iphonesimulator', + darwinArch: 'x86_64', + ), + ]; + + static Target? forFlutterName(String flutterName) { + return all.firstWhereOrNull((element) => element.flutter == flutterName); + } + + static Target? forDarwin({ + required String platformName, + required String darwinAarch, + }) { + return all.firstWhereOrNull((element) => // + element.darwinPlatform == platformName && + element.darwinArch == darwinAarch); + } + + static Target? forRustTriple(String triple) { + return all.firstWhereOrNull((element) => element.rust == triple); + } + + static List androidTargets() { + return all + .where((element) => element.android != null) + .toList(growable: false); + } + + /// Returns buildable targets on current host platform ignoring Android targets. + static List buildableTargets() { + if (Platform.isLinux) { + // Right now we don't support cross-compiling on Linux. So we just return + // the host target. + final arch = runCommand('arch', []).stdout as String; + if (arch.trim() == 'aarch64') { + return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; + } else { + return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; + } + } + return all.where((target) { + if (Platform.isWindows) { + return target.rust.contains('-windows-'); + } else if (Platform.isMacOS) { + return target.darwinPlatform != null; + } + return false; + }).toList(growable: false); + } + + @override + String toString() { + return rust; + } + + final String? flutter; + final String rust; + final String? android; + final int? androidMinSdkVersion; + final String? darwinPlatform; + final String? darwinArch; +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/util.dart b/src/rust_builder/cargokit/build_tool/lib/src/util.dart new file mode 100644 index 0000000..d516a4c --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/util.dart @@ -0,0 +1,116 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'logging.dart'; +import 'rustup.dart'; + +final log = Logger("process"); + +class CommandFailedException implements Exception { + final String executable; + final List arguments; + final ProcessResult result; + + CommandFailedException({ + required this.executable, + required this.arguments, + required this.result, + }); + + @override + String toString() { + final stdout = result.stdout.toString().trim(); + final stderr = result.stderr.toString().trim(); + return [ + "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", + "Returned Exit Code: ${result.exitCode}", + kSeparator, + "STDOUT:", + if (stdout.isNotEmpty) stdout, + kSeparator, + "STDERR:", + if (stderr.isNotEmpty) stderr, + ].join('\n'); + } +} + +ProcessResult runCommand( + String executable, + List arguments, { + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, +}) { + log.finer('Running command $executable ${arguments.join(' ')}'); + final res = Process.runSync( + _resolveExecutable(executable), + arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stderrEncoding: stderrEncoding, + stdoutEncoding: stdoutEncoding, + ); + if (res.exitCode != 0) { + throw CommandFailedException( + executable: executable, + arguments: arguments, + result: res, + ); + } else { + return res; + } +} + +class RustupNotFoundException implements Exception { + @override + String toString() { + return [ + ' ', + 'rustup not found in PATH.', + ' ', + 'Maybe you need to install Rust? It only takes a minute:', + ' ', + if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', + if (hasHomebrewRustInPath()) ...[ + '\$ brew unlink rust # Unlink homebrew Rust from PATH', + ], + if (!Platform.isWindows) + "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + ' ', + ].join('\n'); + } + + static bool hasHomebrewRustInPath() { + if (!Platform.isMacOS) { + return false; + } + final envPath = Platform.environment['PATH'] ?? ''; + final paths = envPath.split(':'); + return paths.any((p) { + return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); + }); + } +} + +String _resolveExecutable(String executable) { + if (executable == 'rustup') { + final resolved = Rustup.executablePath(); + if (resolved != null) { + return resolved; + } + throw RustupNotFoundException(); + } else { + return executable; + } +} diff --git a/src/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart b/src/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart new file mode 100644 index 0000000..2366b57 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/lib/src/verify_binaries.dart @@ -0,0 +1,84 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; + +import 'artifacts_provider.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; + +class VerifyBinaries { + VerifyBinaries({ + required this.manifestDir, + }); + + final String manifestDir; + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final config = CargokitCrateOptions.load(manifestDir: manifestDir); + final precompiledBinaries = config.precompiledBinaries; + if (precompiledBinaries == null) { + stdout.writeln('Crate does not support precompiled binaries.'); + } else { + final crateHash = CrateHash.compute(manifestDir); + stdout.writeln('Crate hash: $crateHash'); + + for (final target in Target.all) { + final message = 'Checking ${target.rust}...'; + stdout.write(message.padRight(40)); + stdout.flush(); + + final artifacts = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + final prefix = precompiledBinaries.uriPrefix; + + bool ok = true; + + for (final artifact in artifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = + Uri.parse('$prefix$crateHash/$signatureFileName'); + + final signature = await get(signatureUrl); + if (signature.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + final asset = await get(url); + if (asset.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + + if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, + signature.bodyBytes)) { + stdout.writeln('INVALID SIGNATURE'); + ok = false; + } + } + + if (ok) { + stdout.writeln('OK'); + } + } + } + } +} diff --git a/src/rust_builder/cargokit/build_tool/pubspec.lock b/src/rust_builder/cargokit/build_tool/pubspec.lock new file mode 100644 index 0000000..343bdd3 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/pubspec.lock @@ -0,0 +1,453 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + args: + dependency: "direct main" + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: "direct main" + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" + crypto: + dependency: "direct main" + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + ed25519_edwards: + dependency: "direct main" + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + github: + dependency: "direct main" + description: + name: github + sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" + url: "https://pub.dev" + source: hosted + version: "9.17.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hex: + dependency: "direct main" + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: "direct main" + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" + url: "https://pub.dev" + source: hosted + version: "1.24.6" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" + url: "https://pub.dev" + source: hosted + version: "0.5.6" + toml: + dependency: "direct main" + description: + name: toml + sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" + url: "https://pub.dev" + source: hosted + version: "0.14.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + version: + dependency: "direct main" + description: + name: version + sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" + url: "https://pub.dev" + source: hosted + version: "11.9.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: "direct main" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/src/rust_builder/cargokit/build_tool/pubspec.yaml b/src/rust_builder/cargokit/build_tool/pubspec.yaml new file mode 100644 index 0000000..18c61e3 --- /dev/null +++ b/src/rust_builder/cargokit/build_tool/pubspec.yaml @@ -0,0 +1,33 @@ +# This is copied from Cargokit (which is the official way to use it currently) +# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +name: build_tool +description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. +publish_to: none +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + +# Add regular dependencies here. +dependencies: + # these are pinned on purpose because the bundle_tool_runner doesn't have + # pubspec.lock. See run_build_tool.sh + logging: 1.2.0 + path: 1.8.0 + version: 3.0.0 + collection: 1.18.0 + ed25519_edwards: 0.3.1 + hex: 0.2.0 + yaml: 3.1.2 + source_span: 1.10.0 + github: 9.17.0 + args: 2.4.2 + crypto: 3.0.3 + convert: 3.1.1 + http: 1.1.0 + toml: 0.14.0 + +dev_dependencies: + lints: ^2.1.0 + test: ^1.24.0 diff --git a/src/rust_builder/cargokit/cmake/cargokit.cmake b/src/rust_builder/cargokit/cmake/cargokit.cmake new file mode 100644 index 0000000..8832600 --- /dev/null +++ b/src/rust_builder/cargokit/cmake/cargokit.cmake @@ -0,0 +1,97 @@ +SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) + +if(WIN32) + # REALPATH does not properly resolve symlinks on windows :-/ + execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Arguments +# - target: CMAKE target to which rust library is linked +# - manifest_dir: relative path from current folder to directory containing cargo manifest +# - lib_name: cargo package name +# - any_symbol_name: name of any exported symbol from the library. +# used on windows to force linking with library. +function(apply_cargokit target manifest_dir lib_name any_symbol_name) + + set(CARGOKIT_LIB_NAME "${lib_name}") + set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") + if (CMAKE_CONFIGURATION_TYPES) + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") + else() + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") + endif() + set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") + + if (FLUTTER_TARGET_PLATFORM) + set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") + else() + set(CARGOKIT_TARGET_PLATFORM "windows-x64") + endif() + + set(CARGOKIT_ENV + "CARGOKIT_CMAKE=${CMAKE_COMMAND}" + "CARGOKIT_CONFIGURATION=$" + "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" + "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" + "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" + "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" + "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" + "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" + ) + + if (WIN32) + set(SCRIPT_EXTENSION ".cmd") + set(IMPORT_LIB_EXTENSION ".lib") + else() + set(SCRIPT_EXTENSION ".sh") + set(IMPORT_LIB_EXTENSION "") + endif() + + # Using generators in custom command is only supported in CMake 3.20+ + if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") + foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) + add_custom_command( + OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endforeach() + else() + add_custom_command( + OUTPUT + ${OUTPUT_LIB} + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endif() + + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) + + if (TARGET ${target}) + # If we have actual cmake target provided create target and make existing + # target depend on it + add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) + add_dependencies("${target}" "${target}_cargokit") + target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") + if(WIN32) + target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") + endif() + else() + # Otherwise (FFI) just use ALL to force building always + add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) + endif() + + # Allow adding the output library to plugin bundled libraries + set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) + +endfunction() \ No newline at end of file diff --git a/src/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/src/rust_builder/cargokit/cmake/resolve_symlinks.ps1 new file mode 100644 index 0000000..3d10d28 --- /dev/null +++ b/src/rust_builder/cargokit/cmake/resolve_symlinks.ps1 @@ -0,0 +1,27 @@ +function Resolve-Symlinks { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string] $Path + ) + + [string] $separator = '/' + [string[]] $parts = $Path.Split($separator) + + [string] $realPath = '' + foreach ($part in $parts) { + if ($realPath -and !$realPath.EndsWith($separator)) { + $realPath += $separator + } + $realPath += $part + $item = Get-Item $realPath + if ($item.Target) { + $realPath = $item.Target.Replace('\', '/') + } + } + $realPath +} + +$path=Resolve-Symlinks -Path $args[0] +Write-Host $path diff --git a/src/rust_builder/cargokit/gradle/plugin.gradle b/src/rust_builder/cargokit/gradle/plugin.gradle new file mode 100644 index 0000000..e8cd17f --- /dev/null +++ b/src/rust_builder/cargokit/gradle/plugin.gradle @@ -0,0 +1,169 @@ +/// This is copied from Cargokit (which is the official way to use it currently) +/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin + +import java.nio.file.Paths +import org.apache.tools.ant.taskdefs.condition.Os + +CargoKitPlugin.file = buildscript.sourceFile + +apply plugin: CargoKitPlugin + +class CargoKitExtension { + String manifestDir; // Relative path to folder containing Cargo.toml + String libname; // Library name within Cargo.toml. Must be a cdylib +} + +abstract class CargoKitBuildTask extends DefaultTask { + + @Input + String buildMode + + @Input + String buildDir + + @Input + String outputDir + + @Input + String ndkVersion + + @Input + String sdkDirectory + + @Input + int compileSdkVersion; + + @Input + int minSdkVersion; + + @Input + String pluginFile + + @Input + List targetPlatforms + + @TaskAction + def build() { + if (project.cargokit.manifestDir == null) { + throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); + } + + if (project.cargokit.libname == null) { + throw new GradleException("Property 'libname' must be set on cargokit extension"); + } + + def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" + def path = Paths.get(new File(pluginFile).parent, "..", executableName); + + def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) + + def rootProjectDir = project.rootProject.projectDir + + project.exec { + executable path + args "build-gradle" + environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir + environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" + environment "CARGOKIT_MANIFEST_DIR", manifestDir + environment "CARGOKIT_CONFIGURATION", buildMode + environment "CARGOKIT_TARGET_TEMP_DIR", buildDir + environment "CARGOKIT_OUTPUT_DIR", outputDir + environment "CARGOKIT_NDK_VERSION", ndkVersion + environment "CARGOKIT_SDK_DIR", sdkDirectory + environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion + environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion + environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") + environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] + } + } +} + +class CargoKitPlugin implements Plugin { + + static String file; + + private Plugin findFlutterPlugin(Project rootProject) { + _findFlutterPlugin(rootProject.childProjects) + } + + private Plugin _findFlutterPlugin(Map projects) { + for (project in projects) { + for (plugin in project.value.getPlugins()) { + if (plugin.class.name == "FlutterPlugin") { + return plugin; + } + } + def plugin = _findFlutterPlugin(project.value.childProjects); + if (plugin != null) { + return plugin; + } + } + return null; + } + + @Override + void apply(Project project) { + def plugin = findFlutterPlugin(project.rootProject); + + project.extensions.create("cargokit", CargoKitExtension) + + if (plugin == null) { + print("Flutter plugin not found, CargoKit plugin will not be applied.") + return; + } + + def cargoBuildDir = "${project.buildDir}/build" + + plugin.project.android.applicationVariants.all { variant -> + + final buildType = variant.buildType.name + + def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; + def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; + jniLibs.srcDir(new File(cargoOutputDir)) + + def platforms = plugin.getTargetPlatforms().collect() + + // Same thing addFlutterDependencies does in flutter.gradle + if (buildType == "debug") { + platforms.add("android-x86") + platforms.add("android-x64") + } + + // The task name depends on plugin properties, which are not available + // at this point + project.getGradle().afterProject { + def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; + + if (project.tasks.findByName(taskName)) { + return + } + + if (plugin.project.android.ndkVersion == null) { + throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") + } + + def task = project.tasks.create(taskName, CargoKitBuildTask.class) { + buildMode = variant.name + buildDir = cargoBuildDir + outputDir = cargoOutputDir + ndkVersion = plugin.project.android.ndkVersion + sdkDirectory = plugin.project.android.sdkDirectory + minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int + compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int + targetPlatforms = platforms + pluginFile = CargoKitPlugin.file + } + def onTask = { newTask -> + if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { + newTask.dependsOn task + // Fix gradle 7.4.2 not picking up JNI library changes + newTask.outputs.upToDateWhen { false } + } + } + project.tasks.each onTask + project.tasks.whenTaskAdded onTask + } + } + } +} diff --git a/src/rust_builder/cargokit/run_build_tool.cmd b/src/rust_builder/cargokit/run_build_tool.cmd new file mode 100644 index 0000000..822e703 --- /dev/null +++ b/src/rust_builder/cargokit/run_build_tool.cmd @@ -0,0 +1,90 @@ +@echo off +setlocal + +setlocal ENABLEDELAYEDEXPANSION + +SET BASEDIR=%~dp0 + +if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( + mkdir "%CARGOKIT_TOOL_TEMP_DIR%" +) +cd "%CARGOKIT_TOOL_TEMP_DIR%" + +SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool +SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart + +set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% + +( + echo name: build_tool_runner + echo version: 1.0.0 + echo publish_to: none + echo. + echo environment: + echo sdk: '^>=3.0.0 ^<4.0.0' + echo. + echo dependencies: + echo build_tool: + echo path: %BUILD_TOOL_PKG_DIR_POSIX% +) >pubspec.yaml + +if not exist bin ( + mkdir bin +) + +( + echo import 'package:build_tool/build_tool.dart' as build_tool; + echo void main^(List^ args^) ^{ + echo build_tool.runMain^(args^); + echo ^} +) >bin\build_tool_runner.dart + +SET PRECOMPILED=bin\build_tool_runner.dill + +REM To detect changes in package we compare output of DIR /s (recursive) +set PREV_PACKAGE_INFO=.dart_tool\package_info.prev +set CUR_PACKAGE_INFO=.dart_tool\package_info.cur + +DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" + +REM Last line in dir output is free space on harddrive. That is bound to +REM change between invocation so we need to remove it +( + Set "Line=" + For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( + SetLocal EnableDelayedExpansion + If Defined Line Echo !Line! + EndLocal + Set "Line=%%A") +) >"%CUR_PACKAGE_INFO%" +DEL "%CUR_PACKAGE_INFO%_orig" + +REM Compare current directory listing with previous +FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 + +If %ERRORLEVEL% neq 0 ( + REM Changed - copy current to previous and remove precompiled kernel + if exist "%PREV_PACKAGE_INFO%" ( + DEL "%PREV_PACKAGE_INFO%" + ) + MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" + if exist "%PRECOMPILED%" ( + DEL "%PRECOMPILED%" + ) +) + +REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% +REM which means we need to do pub get and precompile +if not exist "%PRECOMPILED%" ( + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart +) + +"%DART%" "%PRECOMPILED%" %* + +REM 253 means invalid snapshot version. +If %ERRORLEVEL% equ 253 ( + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart + "%DART%" "%PRECOMPILED%" %* +) diff --git a/src/rust_builder/cargokit/run_build_tool.sh b/src/rust_builder/cargokit/run_build_tool.sh new file mode 100644 index 0000000..0e2c973 --- /dev/null +++ b/src/rust_builder/cargokit/run_build_tool.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +set -e + +BASEDIR=$(dirname "$0") + +mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" + +cd "$CARGOKIT_TOOL_TEMP_DIR" + +# Write a very simple bin package in temp folder that depends on build_tool package +# from Cargokit. This is done to ensure that we don't pollute Cargokit folder +# with .dart_tool contents. + +BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" + +if [[ -z $FLUTTER_ROOT ]]; then # not defined + DART=dart +else + DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" +fi + +cat << EOF > "pubspec.yaml" +name: build_tool_runner +version: 1.0.0 +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + build_tool: + path: "$BUILD_TOOL_PKG_DIR" +EOF + +mkdir -p "bin" + +cat << EOF > "bin/build_tool_runner.dart" +import 'package:build_tool/build_tool.dart' as build_tool; +void main(List args) { + build_tool.runMain(args); +} +EOF + +# Dart run will not cache any package that has a path dependency, which +# is the case for our build_tool_runner. So instead we precompile the package +# ourselves. +# To invalidate the cached kernel we use the hash of ls -LR of the build_tool +# package directory. This should be good enough, as the build_tool package +# itself is not meant to have any path dependencies. + +if [[ "$OSTYPE" == "darwin"* ]]; then + PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) +else + PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) +fi + +PACKAGE_HASH_FILE=".package_hash" + +if [ -f "$PACKAGE_HASH_FILE" ]; then + EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") + if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then + rm "$PACKAGE_HASH_FILE" + fi +fi + +# Run pub get if needed. +if [ ! -f "$PACKAGE_HASH_FILE" ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" +fi + +set +e + +"$DART" bin/build_tool_runner.dill "$@" + +exit_code=$? + +# 253 means invalid snapshot version. +if [ $exit_code == 253 ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + "$DART" bin/build_tool_runner.dill "$@" + exit_code=$? +fi + +exit $exit_code diff --git a/src/rust_builder/ios/Classes/dummy_file.c b/src/rust_builder/ios/Classes/dummy_file.c new file mode 100644 index 0000000..e06dab9 --- /dev/null +++ b/src/rust_builder/ios/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/src/rust_builder/ios/rust_builder.podspec b/src/rust_builder/ios/rust_builder.podspec new file mode 100644 index 0000000..24a8ffd --- /dev/null +++ b/src/rust_builder/ios/rust_builder.podspec @@ -0,0 +1,45 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_builder.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_builder' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib.a', + } +end diff --git a/src/rust_builder/linux/CMakeLists.txt b/src/rust_builder/linux/CMakeLists.txt new file mode 100644 index 0000000..8cabeba --- /dev/null +++ b/src/rust_builder/linux/CMakeLists.txt @@ -0,0 +1,19 @@ +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +# Project-level configuration. +set(PROJECT_NAME "rust_builder") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../rust rust_lib "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_builder_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) diff --git a/src/rust_builder/macos/Classes/dummy_file.c b/src/rust_builder/macos/Classes/dummy_file.c new file mode 100644 index 0000000..e06dab9 --- /dev/null +++ b/src/rust_builder/macos/Classes/dummy_file.c @@ -0,0 +1 @@ +// This is an empty file to force CocoaPods to create a framework. diff --git a/src/rust_builder/macos/rust_builder.podspec b/src/rust_builder/macos/rust_builder.podspec new file mode 100644 index 0000000..3786c16 --- /dev/null +++ b/src/rust_builder/macos/rust_builder.podspec @@ -0,0 +1,44 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint rust_builder.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'rust_builder' + s.version = '0.0.1' + s.summary = 'A new Flutter FFI plugin project.' + s.description = <<-DESC +A new Flutter FFI plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + # This will ensure the source files in Classes/ are included in the native + # builds of apps using this FFI plugin. Podspec does not support relative + # paths, so Classes contains a forwarder C file that relatively imports + # `../src/*` so that the C sources can be shared among all target platforms. + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib.a', + } +end diff --git a/src/rust_builder/pubspec.yaml b/src/rust_builder/pubspec.yaml new file mode 100644 index 0000000..a663567 --- /dev/null +++ b/src/rust_builder/pubspec.yaml @@ -0,0 +1,34 @@ +name: rust_builder +description: "Utility to build Rust code" +version: 0.0.1 +publish_to: none + +environment: + sdk: '>=3.2.0 <4.0.0' + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + ffi: ^2.0.2 + ffigen: ^9.0.0 + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +flutter: + plugin: + platforms: + android: + ffiPlugin: true + ios: + ffiPlugin: true + linux: + ffiPlugin: true + macos: + ffiPlugin: true + windows: + ffiPlugin: true diff --git a/src/rust_builder/windows/.gitignore b/src/rust_builder/windows/.gitignore new file mode 100644 index 0000000..b3eb2be --- /dev/null +++ b/src/rust_builder/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/src/rust_builder/windows/CMakeLists.txt b/src/rust_builder/windows/CMakeLists.txt new file mode 100644 index 0000000..7f37ed7 --- /dev/null +++ b/src/rust_builder/windows/CMakeLists.txt @@ -0,0 +1,20 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "rust_builder") +project(${PROJECT_NAME} LANGUAGES CXX) + +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib "") + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(rust_builder_bundled_libraries + "${${PROJECT_NAME}_cargokit_lib}" + PARENT_SCOPE +) diff --git a/src/test_driver/integration_test.dart b/src/test_driver/integration_test.dart new file mode 100644 index 0000000..b38629c --- /dev/null +++ b/src/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/src/windows/flutter/generated_plugin_registrant.cc b/src/windows/flutter/generated_plugin_registrant.cc index 0333a4b..6f5d3e0 100644 --- a/src/windows/flutter/generated_plugin_registrant.cc +++ b/src/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { EmojiPickerFlutterPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("EmojiPickerFlutterPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/src/windows/flutter/generated_plugins.cmake b/src/windows/flutter/generated_plugins.cmake index 524cbb5..411f594 100644 --- a/src/windows/flutter/generated_plugins.cmake +++ b/src/windows/flutter/generated_plugins.cmake @@ -4,9 +4,11 @@ list(APPEND FLUTTER_PLUGIN_LIST emoji_picker_flutter + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + rust_builder ) set(PLUGIN_BUNDLED_LIBRARIES)