chore: integrate flutter rust bridge

This commit is contained in:
Kingkor Roy Tirtho 2025-11-25 20:49:35 +06:00
parent b254ab6fe2
commit 67cf9805a5
73 changed files with 5468 additions and 137 deletions

3
flutter_rust_bridge.yaml Normal file
View File

@ -0,0 +1,3 @@
rust_input: crate::api
rust_root: rust/
dart_output: lib/src/rust

View File

@ -60,7 +60,7 @@ import 'package:spotube/pages/track/track.dart' as _i35;
/// [_i1.AboutSpotubePage] /// [_i1.AboutSpotubePage]
class AboutSpotubeRoute extends _i41.PageRouteInfo<void> { class AboutSpotubeRoute extends _i41.PageRouteInfo<void> {
const AboutSpotubeRoute({List<_i41.PageRouteInfo>? children}) const AboutSpotubeRoute({List<_i41.PageRouteInfo>? children})
: super(AboutSpotubeRoute.name, initialChildren: children); : super(AboutSpotubeRoute.name, initialChildren: children);
static const String name = 'AboutSpotubeRoute'; static const String name = 'AboutSpotubeRoute';
@ -81,11 +81,11 @@ class AlbumRoute extends _i41.PageRouteInfo<AlbumRouteArgs> {
required _i43.SpotubeSimpleAlbumObject album, required _i43.SpotubeSimpleAlbumObject album,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
AlbumRoute.name, AlbumRoute.name,
args: AlbumRouteArgs(key: key, id: id, album: album), args: AlbumRouteArgs(key: key, id: id, album: album),
rawPathParams: {'id': id}, rawPathParams: {'id': id},
initialChildren: children, initialChildren: children,
); );
static const String name = 'AlbumRoute'; static const String name = 'AlbumRoute';
@ -121,11 +121,11 @@ class ArtistRoute extends _i41.PageRouteInfo<ArtistRouteArgs> {
_i42.Key? key, _i42.Key? key,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
ArtistRoute.name, ArtistRoute.name,
args: ArtistRouteArgs(artistId: artistId, key: key), args: ArtistRouteArgs(artistId: artistId, key: key),
rawPathParams: {'id': artistId}, rawPathParams: {'id': artistId},
initialChildren: children, initialChildren: children,
); );
static const String name = 'ArtistRoute'; static const String name = 'ArtistRoute';
@ -158,7 +158,7 @@ class ArtistRouteArgs {
/// [_i4.BlackListPage] /// [_i4.BlackListPage]
class BlackListRoute extends _i41.PageRouteInfo<void> { class BlackListRoute extends _i41.PageRouteInfo<void> {
const BlackListRoute({List<_i41.PageRouteInfo>? children}) const BlackListRoute({List<_i41.PageRouteInfo>? children})
: super(BlackListRoute.name, initialChildren: children); : super(BlackListRoute.name, initialChildren: children);
static const String name = 'BlackListRoute'; static const String name = 'BlackListRoute';
@ -174,7 +174,7 @@ class BlackListRoute extends _i41.PageRouteInfo<void> {
/// [_i5.ConnectControlPage] /// [_i5.ConnectControlPage]
class ConnectControlRoute extends _i41.PageRouteInfo<void> { class ConnectControlRoute extends _i41.PageRouteInfo<void> {
const ConnectControlRoute({List<_i41.PageRouteInfo>? children}) const ConnectControlRoute({List<_i41.PageRouteInfo>? children})
: super(ConnectControlRoute.name, initialChildren: children); : super(ConnectControlRoute.name, initialChildren: children);
static const String name = 'ConnectControlRoute'; static const String name = 'ConnectControlRoute';
@ -190,7 +190,7 @@ class ConnectControlRoute extends _i41.PageRouteInfo<void> {
/// [_i6.ConnectPage] /// [_i6.ConnectPage]
class ConnectRoute extends _i41.PageRouteInfo<void> { class ConnectRoute extends _i41.PageRouteInfo<void> {
const ConnectRoute({List<_i41.PageRouteInfo>? children}) const ConnectRoute({List<_i41.PageRouteInfo>? children})
: super(ConnectRoute.name, initialChildren: children); : super(ConnectRoute.name, initialChildren: children);
static const String name = 'ConnectRoute'; static const String name = 'ConnectRoute';
@ -206,7 +206,7 @@ class ConnectRoute extends _i41.PageRouteInfo<void> {
/// [_i7.GettingStartedPage] /// [_i7.GettingStartedPage]
class GettingStartedRoute extends _i41.PageRouteInfo<void> { class GettingStartedRoute extends _i41.PageRouteInfo<void> {
const GettingStartedRoute({List<_i41.PageRouteInfo>? children}) const GettingStartedRoute({List<_i41.PageRouteInfo>? children})
: super(GettingStartedRoute.name, initialChildren: children); : super(GettingStartedRoute.name, initialChildren: children);
static const String name = 'GettingStartedRoute'; static const String name = 'GettingStartedRoute';
@ -228,15 +228,15 @@ class HomeBrowseSectionItemsRoute
required _i43.SpotubeBrowseSectionObject<Object> section, required _i43.SpotubeBrowseSectionObject<Object> section,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
HomeBrowseSectionItemsRoute.name, HomeBrowseSectionItemsRoute.name,
args: HomeBrowseSectionItemsRouteArgs( args: HomeBrowseSectionItemsRouteArgs(
key: key, key: key,
sectionId: sectionId, sectionId: sectionId,
section: section, section: section,
), ),
rawPathParams: {'sectionId': sectionId}, rawPathParams: {'sectionId': sectionId},
initialChildren: children, initialChildren: children,
); );
static const String name = 'HomeBrowseSectionItemsRoute'; static const String name = 'HomeBrowseSectionItemsRoute';
@ -276,7 +276,7 @@ class HomeBrowseSectionItemsRouteArgs {
/// [_i9.HomePage] /// [_i9.HomePage]
class HomeRoute extends _i41.PageRouteInfo<void> { class HomeRoute extends _i41.PageRouteInfo<void> {
const HomeRoute({List<_i41.PageRouteInfo>? children}) const HomeRoute({List<_i41.PageRouteInfo>? children})
: super(HomeRoute.name, initialChildren: children); : super(HomeRoute.name, initialChildren: children);
static const String name = 'HomeRoute'; static const String name = 'HomeRoute';
@ -292,7 +292,7 @@ class HomeRoute extends _i41.PageRouteInfo<void> {
/// [_i10.LastFMLoginPage] /// [_i10.LastFMLoginPage]
class LastFMLoginRoute extends _i41.PageRouteInfo<void> { class LastFMLoginRoute extends _i41.PageRouteInfo<void> {
const LastFMLoginRoute({List<_i41.PageRouteInfo>? children}) const LastFMLoginRoute({List<_i41.PageRouteInfo>? children})
: super(LastFMLoginRoute.name, initialChildren: children); : super(LastFMLoginRoute.name, initialChildren: children);
static const String name = 'LastFMLoginRoute'; static const String name = 'LastFMLoginRoute';
@ -308,7 +308,7 @@ class LastFMLoginRoute extends _i41.PageRouteInfo<void> {
/// [_i11.LibraryPage] /// [_i11.LibraryPage]
class LibraryRoute extends _i41.PageRouteInfo<void> { class LibraryRoute extends _i41.PageRouteInfo<void> {
const LibraryRoute({List<_i41.PageRouteInfo>? children}) const LibraryRoute({List<_i41.PageRouteInfo>? children})
: super(LibraryRoute.name, initialChildren: children); : super(LibraryRoute.name, initialChildren: children);
static const String name = 'LibraryRoute'; static const String name = 'LibraryRoute';
@ -328,10 +328,10 @@ class LikedPlaylistRoute extends _i41.PageRouteInfo<LikedPlaylistRouteArgs> {
required _i43.SpotubeSimplePlaylistObject playlist, required _i43.SpotubeSimplePlaylistObject playlist,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
LikedPlaylistRoute.name, LikedPlaylistRoute.name,
args: LikedPlaylistRouteArgs(key: key, playlist: playlist), args: LikedPlaylistRouteArgs(key: key, playlist: playlist),
initialChildren: children, initialChildren: children,
); );
static const String name = 'LikedPlaylistRoute'; static const String name = 'LikedPlaylistRoute';
@ -367,15 +367,15 @@ class LocalLibraryRoute extends _i41.PageRouteInfo<LocalLibraryRouteArgs> {
bool isCache = false, bool isCache = false,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
LocalLibraryRoute.name, LocalLibraryRoute.name,
args: LocalLibraryRouteArgs( args: LocalLibraryRouteArgs(
location: location, location: location,
key: key, key: key,
isDownloads: isDownloads, isDownloads: isDownloads,
isCache: isCache, isCache: isCache,
), ),
initialChildren: children, initialChildren: children,
); );
static const String name = 'LocalLibraryRoute'; static const String name = 'LocalLibraryRoute';
@ -419,7 +419,7 @@ class LocalLibraryRouteArgs {
/// [_i14.LogsPage] /// [_i14.LogsPage]
class LogsRoute extends _i41.PageRouteInfo<void> { class LogsRoute extends _i41.PageRouteInfo<void> {
const LogsRoute({List<_i41.PageRouteInfo>? children}) const LogsRoute({List<_i41.PageRouteInfo>? children})
: super(LogsRoute.name, initialChildren: children); : super(LogsRoute.name, initialChildren: children);
static const String name = 'LogsRoute'; static const String name = 'LogsRoute';
@ -435,7 +435,7 @@ class LogsRoute extends _i41.PageRouteInfo<void> {
/// [_i15.LyricsPage] /// [_i15.LyricsPage]
class LyricsRoute extends _i41.PageRouteInfo<void> { class LyricsRoute extends _i41.PageRouteInfo<void> {
const LyricsRoute({List<_i41.PageRouteInfo>? children}) const LyricsRoute({List<_i41.PageRouteInfo>? children})
: super(LyricsRoute.name, initialChildren: children); : super(LyricsRoute.name, initialChildren: children);
static const String name = 'LyricsRoute'; static const String name = 'LyricsRoute';
@ -455,10 +455,10 @@ class MiniLyricsRoute extends _i41.PageRouteInfo<MiniLyricsRouteArgs> {
required _i44.Size prevSize, required _i44.Size prevSize,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
MiniLyricsRoute.name, MiniLyricsRoute.name,
args: MiniLyricsRouteArgs(key: key, prevSize: prevSize), args: MiniLyricsRouteArgs(key: key, prevSize: prevSize),
initialChildren: children, initialChildren: children,
); );
static const String name = 'MiniLyricsRoute'; static const String name = 'MiniLyricsRoute';
@ -488,7 +488,7 @@ class MiniLyricsRouteArgs {
/// [_i17.PlayerLyricsPage] /// [_i17.PlayerLyricsPage]
class PlayerLyricsRoute extends _i41.PageRouteInfo<void> { class PlayerLyricsRoute extends _i41.PageRouteInfo<void> {
const PlayerLyricsRoute({List<_i41.PageRouteInfo>? children}) const PlayerLyricsRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerLyricsRoute.name, initialChildren: children); : super(PlayerLyricsRoute.name, initialChildren: children);
static const String name = 'PlayerLyricsRoute'; static const String name = 'PlayerLyricsRoute';
@ -504,7 +504,7 @@ class PlayerLyricsRoute extends _i41.PageRouteInfo<void> {
/// [_i18.PlayerQueuePage] /// [_i18.PlayerQueuePage]
class PlayerQueueRoute extends _i41.PageRouteInfo<void> { class PlayerQueueRoute extends _i41.PageRouteInfo<void> {
const PlayerQueueRoute({List<_i41.PageRouteInfo>? children}) const PlayerQueueRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerQueueRoute.name, initialChildren: children); : super(PlayerQueueRoute.name, initialChildren: children);
static const String name = 'PlayerQueueRoute'; static const String name = 'PlayerQueueRoute';
@ -520,7 +520,7 @@ class PlayerQueueRoute extends _i41.PageRouteInfo<void> {
/// [_i19.PlayerTrackSourcesPage] /// [_i19.PlayerTrackSourcesPage]
class PlayerTrackSourcesRoute extends _i41.PageRouteInfo<void> { class PlayerTrackSourcesRoute extends _i41.PageRouteInfo<void> {
const PlayerTrackSourcesRoute({List<_i41.PageRouteInfo>? children}) const PlayerTrackSourcesRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerTrackSourcesRoute.name, initialChildren: children); : super(PlayerTrackSourcesRoute.name, initialChildren: children);
static const String name = 'PlayerTrackSourcesRoute'; static const String name = 'PlayerTrackSourcesRoute';
@ -541,11 +541,11 @@ class PlaylistRoute extends _i41.PageRouteInfo<PlaylistRouteArgs> {
required _i43.SpotubeSimplePlaylistObject playlist, required _i43.SpotubeSimplePlaylistObject playlist,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
PlaylistRoute.name, PlaylistRoute.name,
args: PlaylistRouteArgs(key: key, id: id, playlist: playlist), args: PlaylistRouteArgs(key: key, id: id, playlist: playlist),
rawPathParams: {'id': id}, rawPathParams: {'id': id},
initialChildren: children, initialChildren: children,
); );
static const String name = 'PlaylistRoute'; static const String name = 'PlaylistRoute';
@ -581,7 +581,7 @@ class PlaylistRouteArgs {
/// [_i21.ProfilePage] /// [_i21.ProfilePage]
class ProfileRoute extends _i41.PageRouteInfo<void> { class ProfileRoute extends _i41.PageRouteInfo<void> {
const ProfileRoute({List<_i41.PageRouteInfo>? children}) const ProfileRoute({List<_i41.PageRouteInfo>? children})
: super(ProfileRoute.name, initialChildren: children); : super(ProfileRoute.name, initialChildren: children);
static const String name = 'ProfileRoute'; static const String name = 'ProfileRoute';
@ -597,7 +597,7 @@ class ProfileRoute extends _i41.PageRouteInfo<void> {
/// [_i22.RootAppPage] /// [_i22.RootAppPage]
class RootAppRoute extends _i41.PageRouteInfo<void> { class RootAppRoute extends _i41.PageRouteInfo<void> {
const RootAppRoute({List<_i41.PageRouteInfo>? children}) const RootAppRoute({List<_i41.PageRouteInfo>? children})
: super(RootAppRoute.name, initialChildren: children); : super(RootAppRoute.name, initialChildren: children);
static const String name = 'RootAppRoute'; static const String name = 'RootAppRoute';
@ -613,7 +613,7 @@ class RootAppRoute extends _i41.PageRouteInfo<void> {
/// [_i23.SearchPage] /// [_i23.SearchPage]
class SearchRoute extends _i41.PageRouteInfo<void> { class SearchRoute extends _i41.PageRouteInfo<void> {
const SearchRoute({List<_i41.PageRouteInfo>? children}) const SearchRoute({List<_i41.PageRouteInfo>? children})
: super(SearchRoute.name, initialChildren: children); : super(SearchRoute.name, initialChildren: children);
static const String name = 'SearchRoute'; static const String name = 'SearchRoute';
@ -635,14 +635,14 @@ class SettingsMetadataProviderFormRoute
required List<_i43.MetadataFormFieldObject> fields, required List<_i43.MetadataFormFieldObject> fields,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
SettingsMetadataProviderFormRoute.name, SettingsMetadataProviderFormRoute.name,
args: SettingsMetadataProviderFormRouteArgs( args: SettingsMetadataProviderFormRouteArgs(
key: key, key: key,
title: title, title: title,
fields: fields, fields: fields,
), ),
initialChildren: children, initialChildren: children,
); );
static const String name = 'SettingsMetadataProviderFormRoute'; static const String name = 'SettingsMetadataProviderFormRoute';
@ -682,7 +682,7 @@ class SettingsMetadataProviderFormRouteArgs {
/// [_i25.SettingsMetadataProviderPage] /// [_i25.SettingsMetadataProviderPage]
class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> { class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> {
const SettingsMetadataProviderRoute({List<_i41.PageRouteInfo>? children}) const SettingsMetadataProviderRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsMetadataProviderRoute.name, initialChildren: children); : super(SettingsMetadataProviderRoute.name, initialChildren: children);
static const String name = 'SettingsMetadataProviderRoute'; static const String name = 'SettingsMetadataProviderRoute';
@ -698,7 +698,7 @@ class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> {
/// [_i26.SettingsPage] /// [_i26.SettingsPage]
class SettingsRoute extends _i41.PageRouteInfo<void> { class SettingsRoute extends _i41.PageRouteInfo<void> {
const SettingsRoute({List<_i41.PageRouteInfo>? children}) const SettingsRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsRoute.name, initialChildren: children); : super(SettingsRoute.name, initialChildren: children);
static const String name = 'SettingsRoute'; static const String name = 'SettingsRoute';
@ -714,7 +714,7 @@ class SettingsRoute extends _i41.PageRouteInfo<void> {
/// [_i27.SettingsScrobblingPage] /// [_i27.SettingsScrobblingPage]
class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> { class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> {
const SettingsScrobblingRoute({List<_i41.PageRouteInfo>? children}) const SettingsScrobblingRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsScrobblingRoute.name, initialChildren: children); : super(SettingsScrobblingRoute.name, initialChildren: children);
static const String name = 'SettingsScrobblingRoute'; static const String name = 'SettingsScrobblingRoute';
@ -730,7 +730,7 @@ class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> {
/// [_i28.StatsAlbumsPage] /// [_i28.StatsAlbumsPage]
class StatsAlbumsRoute extends _i41.PageRouteInfo<void> { class StatsAlbumsRoute extends _i41.PageRouteInfo<void> {
const StatsAlbumsRoute({List<_i41.PageRouteInfo>? children}) const StatsAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsAlbumsRoute.name, initialChildren: children); : super(StatsAlbumsRoute.name, initialChildren: children);
static const String name = 'StatsAlbumsRoute'; static const String name = 'StatsAlbumsRoute';
@ -746,7 +746,7 @@ class StatsAlbumsRoute extends _i41.PageRouteInfo<void> {
/// [_i29.StatsArtistsPage] /// [_i29.StatsArtistsPage]
class StatsArtistsRoute extends _i41.PageRouteInfo<void> { class StatsArtistsRoute extends _i41.PageRouteInfo<void> {
const StatsArtistsRoute({List<_i41.PageRouteInfo>? children}) const StatsArtistsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsArtistsRoute.name, initialChildren: children); : super(StatsArtistsRoute.name, initialChildren: children);
static const String name = 'StatsArtistsRoute'; static const String name = 'StatsArtistsRoute';
@ -762,7 +762,7 @@ class StatsArtistsRoute extends _i41.PageRouteInfo<void> {
/// [_i30.StatsMinutesPage] /// [_i30.StatsMinutesPage]
class StatsMinutesRoute extends _i41.PageRouteInfo<void> { class StatsMinutesRoute extends _i41.PageRouteInfo<void> {
const StatsMinutesRoute({List<_i41.PageRouteInfo>? children}) const StatsMinutesRoute({List<_i41.PageRouteInfo>? children})
: super(StatsMinutesRoute.name, initialChildren: children); : super(StatsMinutesRoute.name, initialChildren: children);
static const String name = 'StatsMinutesRoute'; static const String name = 'StatsMinutesRoute';
@ -778,7 +778,7 @@ class StatsMinutesRoute extends _i41.PageRouteInfo<void> {
/// [_i31.StatsPage] /// [_i31.StatsPage]
class StatsRoute extends _i41.PageRouteInfo<void> { class StatsRoute extends _i41.PageRouteInfo<void> {
const StatsRoute({List<_i41.PageRouteInfo>? children}) const StatsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsRoute.name, initialChildren: children); : super(StatsRoute.name, initialChildren: children);
static const String name = 'StatsRoute'; static const String name = 'StatsRoute';
@ -794,7 +794,7 @@ class StatsRoute extends _i41.PageRouteInfo<void> {
/// [_i32.StatsPlaylistsPage] /// [_i32.StatsPlaylistsPage]
class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> { class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> {
const StatsPlaylistsRoute({List<_i41.PageRouteInfo>? children}) const StatsPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsPlaylistsRoute.name, initialChildren: children); : super(StatsPlaylistsRoute.name, initialChildren: children);
static const String name = 'StatsPlaylistsRoute'; static const String name = 'StatsPlaylistsRoute';
@ -810,7 +810,7 @@ class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> {
/// [_i33.StatsStreamFeesPage] /// [_i33.StatsStreamFeesPage]
class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> { class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> {
const StatsStreamFeesRoute({List<_i41.PageRouteInfo>? children}) const StatsStreamFeesRoute({List<_i41.PageRouteInfo>? children})
: super(StatsStreamFeesRoute.name, initialChildren: children); : super(StatsStreamFeesRoute.name, initialChildren: children);
static const String name = 'StatsStreamFeesRoute'; static const String name = 'StatsStreamFeesRoute';
@ -826,7 +826,7 @@ class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> {
/// [_i34.StatsStreamsPage] /// [_i34.StatsStreamsPage]
class StatsStreamsRoute extends _i41.PageRouteInfo<void> { class StatsStreamsRoute extends _i41.PageRouteInfo<void> {
const StatsStreamsRoute({List<_i41.PageRouteInfo>? children}) const StatsStreamsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsStreamsRoute.name, initialChildren: children); : super(StatsStreamsRoute.name, initialChildren: children);
static const String name = 'StatsStreamsRoute'; static const String name = 'StatsStreamsRoute';
@ -846,11 +846,11 @@ class TrackRoute extends _i41.PageRouteInfo<TrackRouteArgs> {
required String trackId, required String trackId,
List<_i41.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
TrackRoute.name, TrackRoute.name,
args: TrackRouteArgs(key: key, trackId: trackId), args: TrackRouteArgs(key: key, trackId: trackId),
rawPathParams: {'id': trackId}, rawPathParams: {'id': trackId},
initialChildren: children, initialChildren: children,
); );
static const String name = 'TrackRoute'; static const String name = 'TrackRoute';
@ -883,7 +883,7 @@ class TrackRouteArgs {
/// [_i36.UserAlbumsPage] /// [_i36.UserAlbumsPage]
class UserAlbumsRoute extends _i41.PageRouteInfo<void> { class UserAlbumsRoute extends _i41.PageRouteInfo<void> {
const UserAlbumsRoute({List<_i41.PageRouteInfo>? children}) const UserAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super(UserAlbumsRoute.name, initialChildren: children); : super(UserAlbumsRoute.name, initialChildren: children);
static const String name = 'UserAlbumsRoute'; static const String name = 'UserAlbumsRoute';
@ -899,7 +899,7 @@ class UserAlbumsRoute extends _i41.PageRouteInfo<void> {
/// [_i37.UserArtistsPage] /// [_i37.UserArtistsPage]
class UserArtistsRoute extends _i41.PageRouteInfo<void> { class UserArtistsRoute extends _i41.PageRouteInfo<void> {
const UserArtistsRoute({List<_i41.PageRouteInfo>? children}) const UserArtistsRoute({List<_i41.PageRouteInfo>? children})
: super(UserArtistsRoute.name, initialChildren: children); : super(UserArtistsRoute.name, initialChildren: children);
static const String name = 'UserArtistsRoute'; static const String name = 'UserArtistsRoute';
@ -915,7 +915,7 @@ class UserArtistsRoute extends _i41.PageRouteInfo<void> {
/// [_i38.UserDownloadsPage] /// [_i38.UserDownloadsPage]
class UserDownloadsRoute extends _i41.PageRouteInfo<void> { class UserDownloadsRoute extends _i41.PageRouteInfo<void> {
const UserDownloadsRoute({List<_i41.PageRouteInfo>? children}) const UserDownloadsRoute({List<_i41.PageRouteInfo>? children})
: super(UserDownloadsRoute.name, initialChildren: children); : super(UserDownloadsRoute.name, initialChildren: children);
static const String name = 'UserDownloadsRoute'; static const String name = 'UserDownloadsRoute';
@ -931,7 +931,7 @@ class UserDownloadsRoute extends _i41.PageRouteInfo<void> {
/// [_i39.UserLocalLibraryPage] /// [_i39.UserLocalLibraryPage]
class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> { class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> {
const UserLocalLibraryRoute({List<_i41.PageRouteInfo>? children}) const UserLocalLibraryRoute({List<_i41.PageRouteInfo>? children})
: super(UserLocalLibraryRoute.name, initialChildren: children); : super(UserLocalLibraryRoute.name, initialChildren: children);
static const String name = 'UserLocalLibraryRoute'; static const String name = 'UserLocalLibraryRoute';
@ -947,7 +947,7 @@ class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> {
/// [_i40.UserPlaylistsPage] /// [_i40.UserPlaylistsPage]
class UserPlaylistsRoute extends _i41.PageRouteInfo<void> { class UserPlaylistsRoute extends _i41.PageRouteInfo<void> {
const UserPlaylistsRoute({List<_i41.PageRouteInfo>? children}) const UserPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super(UserPlaylistsRoute.name, initialChildren: children); : super(UserPlaylistsRoute.name, initialChildren: children);
static const String name = 'UserPlaylistsRoute'; static const String name = 'UserPlaylistsRoute';

View File

@ -71,7 +71,8 @@ class PlaybuttonTile extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Tooltip( Tooltip(
tooltip: TooltipContainer(child: Text(context.l10n.add_to_queue)).call, tooltip:
TooltipContainer(child: Text(context.l10n.add_to_queue)).call,
child: IconButton.outline( child: IconButton.outline(
icon: const Icon(SpotubeIcons.queueAdd), icon: const Icon(SpotubeIcons.queueAdd),
onPressed: onAddToQueuePressed, onPressed: onAddToQueuePressed,

View File

@ -233,36 +233,36 @@ class TrackTile extends HookConsumerWidget {
child: AbsorbPointer( child: AbsorbPointer(
absorbing: selectionMode, absorbing: selectionMode,
child: switch (track) { child: switch (track) {
SpotubeLocalTrackObject() => Text( SpotubeLocalTrackObject() => Text(
track.name, track.name,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
_ => Row( _ => Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Flexible( Flexible(
child: Button( child: Button(
style: ButtonVariance.link.copyWith( style: ButtonVariance.link.copyWith(
padding: (context, states, value) => padding: (context, states, value) =>
EdgeInsets.zero, EdgeInsets.zero,
), ),
onPressed: effectiveSelection onPressed: effectiveSelection
? null ? null
: () { : () {
context context.navigateTo(
.navigateTo(TrackRoute(trackId: track.id)); TrackRoute(trackId: track.id));
}, },
child: Text( child: Text(
track.name, track.name,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
),
), ),
), ),
), ],
], ),
), },
},
), ),
), ),
if (constrains.mdAndUp) ...[ if (constrains.mdAndUp) ...[
@ -294,14 +294,14 @@ class TrackTile extends HookConsumerWidget {
), ),
subtitle: Align( subtitle: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: track is SpotubeLocalTrackObject child: track is SpotubeLocalTrackObject
? Text( ? Text(
track.artists.asString(), track.artists.asString(),
) )
: ClipRect( : ClipRect(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 40), constraints: const BoxConstraints(maxHeight: 40),
child: AbsorbPointer( child: AbsorbPointer(
absorbing: effectiveSelection, absorbing: effectiveSelection,
child: ArtistLink( child: ArtistLink(
artists: track.artists, artists: track.artists,

View File

@ -1,14 +1,6 @@
import 'dart:async';
import 'package:app_links/app_links.dart'; import 'package:app_links/app_links.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/routes.dart'; import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
import 'package:flutter_sharing_intent/model/sharing_file.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/utils/platform.dart';
final appLinks = AppLinks(); final appLinks = AppLinks();
final linkStream = appLinks.stringLinkStream.asBroadcastStream(); final linkStream = appLinks.stringLinkStream.asBroadcastStream();

View File

@ -1,4 +1,3 @@
import 'package:flutter/services.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';

View File

@ -1,4 +1,3 @@
import 'package:flutter/services.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';

View File

@ -1,10 +1,5 @@
import 'package:flutter_undraw/flutter_undraw.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
@Deprecated( @Deprecated(
"Later a featured playlists API will be added for metadata plugins.") "Later a featured playlists API will be added for metadata plugins.")

View File

@ -1,5 +1,4 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/foundation.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/formatters.dart'; import 'package:spotube/collections/formatters.dart';

View File

@ -168,7 +168,7 @@ class MiniLyricsPage extends HookConsumerWidget {
child: Column( child: Column(
children: [ children: [
if (playlistQueue.activeTrack != null) if (playlistQueue.activeTrack != null)
Text(playlistQueue.activeTrack!.name!).semiBold(), Text(playlistQueue.activeTrack!.name).semiBold(),
if (showLyrics.value) if (showLyrics.value)
Expanded( Expanded(
child: IndexedStack( child: IndexedStack(

View File

@ -7,7 +7,7 @@ class MetadataPluginRepositoriesNotifier
extends PaginatedAsyncNotifier<MetadataPluginRepository> { extends PaginatedAsyncNotifier<MetadataPluginRepository> {
MetadataPluginRepositoriesNotifier() : super(); MetadataPluginRepositoriesNotifier() : super();
Map<String, bool> _hasMore = {}; final Map<String, bool> _hasMore = {};
@override @override
fetch(int offset, int limit) async { fetch(int offset, int limit) async {

View File

@ -84,8 +84,8 @@ class WindowsAudioService {
title: track.name, title: track.name,
albumArtist: track.artists.firstOrNull?.name ?? "Unknown", albumArtist: track.artists.firstOrNull?.name ?? "Unknown",
artist: track.artists.asString(), artist: track.artists.asString(),
album: track.album?.name ?? "Unknown", album: track.album.name ?? "Unknown",
thumbnail: (track.album?.images).asUrlString( thumbnail: (track.album.images).asUrlString(
placeholder: ImagePlaceholder.albumArt, placeholder: ImagePlaceholder.albumArt,
), ),
), ),

View File

@ -0,0 +1,10 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// 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';
String greet({required String name}) =>
RustLib.instance.api.crateApiSimpleGreet(name: name);

View File

@ -0,0 +1,240 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// 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, unused_field
import 'api/simple.dart';
import 'dart:async';
import 'dart:convert';
import 'frb_generated.dart';
import 'frb_generated.io.dart'
if (dart.library.js_interop) 'frb_generated.web.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
/// Main entrypoint of the Rust API
class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
@internal
static final instance = RustLib._();
RustLib._();
/// Initialize flutter_rust_bridge
static Future<void> init({
RustLibApi? api,
BaseHandler? handler,
ExternalLibrary? externalLibrary,
bool forceSameCodegenVersion = true,
}) async {
await instance.initImpl(
api: api,
handler: handler,
externalLibrary: externalLibrary,
forceSameCodegenVersion: forceSameCodegenVersion,
);
}
/// Initialize flutter_rust_bridge in mock mode.
/// No libraries for FFI are loaded.
static void initMock({
required RustLibApi api,
}) {
instance.initMockImpl(
api: api,
);
}
/// 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<RustLibApiImpl, RustLibWire> get apiImplConstructor =>
RustLibApiImpl.new;
@override
WireConstructor<RustLibWire> get wireConstructor =>
RustLibWire.fromExternalLibrary;
@override
Future<void> executeRustInitializers() async {
await api.crateApiSimpleInitApp();
}
@override
ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig =>
kDefaultExternalLibraryLoaderConfig;
@override
String get codegenVersion => '2.11.1';
@override
int get rustContentHash => -1918914929;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
stem: 'rust_lib_spotube',
ioDirectory: 'rust/target/release/',
webPrefix: 'pkg/',
);
}
abstract class RustLibApi extends BaseApi {
String crateApiSimpleGreet({required String name});
Future<void> crateApiSimpleInitApp();
}
class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
RustLibApiImpl({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@override
String crateApiSimpleGreet({required String name}) {
return handler.executeSync(SyncTask(
callFfi: () {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_String(name, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1)!;
},
codec: SseCodec(
decodeSuccessData: sse_decode_String,
decodeErrorData: null,
),
constMeta: kCrateApiSimpleGreetConstMeta,
argValues: [name],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta(
debugName: "greet",
argNames: ["name"],
);
@override
Future<void> crateApiSimpleInitApp() {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(generalizedFrbRustBinding, serializer,
funcId: 2, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiSimpleInitAppConstMeta,
argValues: [],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta(
debugName: "init_app",
argNames: [],
);
@protected
String dco_decode_String(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as String;
}
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as Uint8List;
}
@protected
int dco_decode_u_8(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as int;
}
@protected
void dco_decode_unit(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return;
}
@protected
String sse_decode_String(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var inner = sse_decode_list_prim_u_8_strict(deserializer);
return utf8.decoder.convert(inner);
}
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
return deserializer.buffer.getUint8List(len_);
}
@protected
int sse_decode_u_8(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint8();
}
@protected
void sse_decode_unit(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
}
@protected
int sse_decode_i_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getInt32();
}
@protected
bool sse_decode_bool(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint8() != 0;
}
@protected
void sse_encode_String(String self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer);
}
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
serializer.buffer.putUint8List(self);
}
@protected
void sse_encode_u_8(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint8(self);
}
@protected
void sse_encode_unit(void self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
}
@protected
void sse_encode_i_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putInt32(self);
}
@protected
void sse_encode_bool(bool self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint8(self ? 1 : 0);
}
}

View File

@ -0,0 +1,84 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// 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, unused_field
import 'api/simple.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';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
String dco_decode_String(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
}
// Section: wire_class
class RustLibWire implements BaseWire {
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
RustLibWire(lib.ffiDynamicLibrary);
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
}

View File

@ -0,0 +1,84 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// 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, unused_field
// Static analysis wrongly picks the IO variant, thus ignore this
// ignore_for_file: argument_type_not_assignable
import 'api/simple.dart';
import 'dart:async';
import 'dart:convert';
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
String dco_decode_String(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
}
// Section: wire_class
class RustLibWire implements BaseWire {
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
}
@JS('wasm_bindgen')
external RustLibWasmModule get wasmModule;
@JS()
@anonymous
extension type RustLibWasmModule._(JSObject _) implements JSObject {}

View File

@ -22,6 +22,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_discord_rpc flutter_discord_rpc
metadata_god metadata_god
rust_lib_spotube
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -968,7 +968,7 @@ packages:
source: hosted source: hosted
version: "2.6.1" version: "2.6.1"
flutter_rust_bridge: flutter_rust_bridge:
dependency: transitive dependency: "direct main"
description: description:
name: flutter_rust_bridge name: flutter_rust_bridge
sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e" sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e"
@ -1958,6 +1958,13 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.1" version: "2.6.1"
rust_lib_spotube:
dependency: "direct main"
description:
path: rust_builder
relative: true
source: path
version: "0.0.1"
rxdart: rxdart:
dependency: transitive dependency: transitive
description: description:

View File

@ -153,6 +153,9 @@ dependencies:
url: https://github.com/media-kit/media-kit url: https://github.com/media-kit/media-kit
path: libs/universal/media_kit_libs_audio path: libs/universal/media_kit_libs_audio
base32: ^2.2.0 base32: ^2.2.0
rust_lib_spotube:
path: rust_builder
flutter_rust_bridge: 2.11.1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.13 build_runner: ^2.4.13

1
rust/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

729
rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,729 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "allo-isolate"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449e356a4864c017286dbbec0e12767ea07efba29e3b7d984194c2a7ff3c4550"
dependencies = [
"anyhow",
"atomic",
"backtrace",
]
[[package]]
name = "android_log-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
[[package]]
name = "android_logger"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3"
dependencies = [
"android_log-sys",
"env_filter",
"log",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "atomic"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "build-target"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b"
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "dart-sys"
version = "4.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57967e4b200d767d091b961d6ab42cc7d0cc14fe9e052e75d0d3cf9eb732d895"
dependencies = [
"cc",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "delegate-attr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51aac4c99b2e6775164b412ea33ae8441b2fde2dbf05a20bc0052a63d08c475b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "env_filter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2"
dependencies = [
"log",
"regex",
]
[[package]]
name = "flutter_rust_bridge"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde126295b2acc5f0a712e265e91b6fdc0ed38767496483e592ae7134db83725"
dependencies = [
"allo-isolate",
"android_logger",
"anyhow",
"build-target",
"bytemuck",
"byteorder",
"console_error_panic_hook",
"dart-sys",
"delegate-attr",
"flutter_rust_bridge_macros",
"futures",
"js-sys",
"lazy_static",
"log",
"oslog",
"portable-atomic",
"threadpool",
"tokio",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "flutter_rust_bridge_macros"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f0420326b13675321b194928bb7830043b68cf8b810e1c651285c747abb080"
dependencies = [
"hex",
"md-5",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-macro"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]]
name = "futures-task"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-util"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oslog"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969"
dependencies = [
"cc",
"dashmap",
"log",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rust_lib_spotube"
version = "0.1.0"
dependencies = [
"flutter_rust_bridge",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "threadpool"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [
"num_cpus",
]
[[package]]
name = "tokio"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [
"backtrace",
"num_cpus",
"pin-project-lite",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"

13
rust/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "rust_lib_spotube"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
flutter_rust_bridge = "=2.11.1"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }

1
rust/src/api/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod simple;

10
rust/src/api/simple.rs Normal file
View File

@ -0,0 +1,10 @@
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: String) -> String {
format!("Hello, {name}!")
}
#[flutter_rust_bridge::frb(init)]
pub fn init_app() {
// Default utilities - feel free to customize
flutter_rust_bridge::setup_default_user_utils();
}

276
rust/src/frb_generated.rs Normal file
View File

@ -0,0 +1,276 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
#![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,
clippy::match_single_binding,
clippy::clone_on_copy,
clippy::let_unit_value,
clippy::deref_addrof,
clippy::explicit_auto_deref,
clippy::borrow_deref_ref,
clippy::needless_borrow
)]
// Section: imports
use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
use flutter_rust_bridge::{Handler, IntoIntoDart};
// Section: boilerplate
flutter_rust_bridge::frb_generated_boilerplate!(
default_stream_sink_codec = SseCodec,
default_rust_opaque = RustOpaqueMoi,
default_rust_auto_opaque = RustAutoOpaqueMoi,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1918914929;
// Section: executor
flutter_rust_bridge::frb_generated_default_handler!();
// Section: wire_funcs
fn wire__crate__api__simple__greet_impl(
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::<flutter_rust_bridge::for_generated::SseCodec, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "greet",
port: None,
mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
let api_name = <String>::sse_decode(&mut deserializer);
deserializer.end();
transform_result_sse::<_, ()>((move || {
let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?;
Ok(output_ok)
})())
},
)
}
fn wire__crate__api__simple__init_app_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len_: i32,
data_len_: i32,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::SseCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "init_app",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let message = unsafe {
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
ptr_,
rust_vec_len_,
data_len_,
)
};
let mut deserializer =
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
deserializer.end();
move |context| {
transform_result_sse::<_, ()>((move || {
let output_ok = Result::<_, ()>::Ok({
crate::api::simple::init_app();
})?;
Ok(output_ok)
})())
}
},
)
}
// Section: dart2rust
impl SseDecode for String {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut inner = <Vec<u8>>::sse_decode(deserializer);
return String::from_utf8(inner).unwrap();
}
}
impl SseDecode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut len_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<u8>::sse_decode(deserializer));
}
return ans_;
}
}
impl SseDecode for u8 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u8().unwrap()
}
}
impl SseDecode for () {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
}
impl SseDecode for i32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_i32::<NativeEndian>().unwrap()
}
}
impl SseDecode for bool {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u8().unwrap() != 0
}
}
fn pde_ffi_dispatcher_primary_impl(
func_id: i32,
port: flutter_rust_bridge::for_generated::MessagePort,
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len: i32,
data_len: i32,
) {
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
fn pde_ffi_dispatcher_sync_impl(
func_id: i32,
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len: i32,
data_len: i32,
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
1 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
// Section: rust2dart
impl SseEncode for String {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<Vec<u8>>::sse_encode(self.into_bytes(), serializer);
}
}
impl SseEncode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<u8>::sse_encode(item, serializer);
}
}
}
impl SseEncode for u8 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_u8(self).unwrap();
}
}
impl SseEncode for () {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
}
impl SseEncode for i32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_i32::<NativeEndian>(self).unwrap();
}
}
impl SseEncode for bool {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_u8(self as _).unwrap();
}
}
#[cfg(not(target_family = "wasm"))]
mod io {
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// Section: imports
use super::*;
use flutter_rust_bridge::for_generated::byteorder::{
NativeEndian, ReadBytesExt, WriteBytesExt,
};
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
use flutter_rust_bridge::{Handler, IntoIntoDart};
// Section: boilerplate
flutter_rust_bridge::frb_generated_boilerplate_io!();
}
#[cfg(not(target_family = "wasm"))]
pub use io::*;
/// cbindgen:ignore
#[cfg(target_family = "wasm")]
mod web {
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// Section: imports
use super::*;
use flutter_rust_bridge::for_generated::byteorder::{
NativeEndian, ReadBytesExt, WriteBytesExt,
};
use flutter_rust_bridge::for_generated::wasm_bindgen;
use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*;
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
use flutter_rust_bridge::{Handler, IntoIntoDart};
// Section: boilerplate
flutter_rust_bridge::frb_generated_boilerplate_web!();
}
#[cfg(target_family = "wasm")]
pub use web::*;

2
rust/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod api;
mod frb_generated;

29
rust_builder/.gitignore vendored Normal file
View File

@ -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/

1
rust_builder/README.md Normal file
View File

@ -0,0 +1 @@
Please ignore this folder, which is just glue to build Rust with Flutter.

9
rust_builder/android/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View File

@ -0,0 +1,56 @@
// The Android Gradle Plugin builds the native code with the Android NDK.
group 'com.flutter_rust_bridge.rust_lib_spotube'
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_lib_spotube'
}
// 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_spotube"
}

View File

@ -0,0 +1 @@
rootProject.name = 'rust_lib_spotube'

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flutter_rust_bridge.rust_lib_spotube">
</manifest>

4
rust_builder/cargokit/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
target
.dart_tool
*.iml
!pubspec.lock

View File

@ -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.

View File

@ -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.

View File

@ -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
sh "$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"

View File

@ -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/`.

View File

@ -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

View File

@ -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<String> arguments) {
build_tool.runMain(arguments);
}

View File

@ -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<void> runMain(List<String> args) async {
return build_tool.runMain(args);
}

View File

@ -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<String> 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<Map<String, String>> 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 cxxFlagsKey = '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,
cxxFlagsKey: 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;
}
}

View File

@ -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<Map<Target, List<Artifact>>> getArtifacts(List<Target> 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 = <String>{
...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<Map<Target, List<Artifact>>> _getPrecompiledArtifacts(
List<Target> 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 = <Target, List<Artifact>>{};
for (final target in targets) {
final requiredArtifacts = getArtifactNames(
target: target,
libraryName: environment.crateInfo.packageName,
remote: true,
);
final artifactsForTarget = <Artifact>[];
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<Response> _get(Uri url, {Map<String, String>? 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<void> _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<String> 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}");
}
}

View File

@ -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<void> 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));
}
}
}
}

View File

@ -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<void> 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));
}
}
}
}
}

View File

@ -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<void> 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<String> 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');
}
}
}

View File

@ -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<void> runBuildCommand(CargokitUserOptions options);
@override
Future<void> 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<void> 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<void> 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<void> 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<void> 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<String>;
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<void> run() async {
final manifestDir = argResults!['manifest-dir'] as String;
final verifyBinaries = VerifyBinaries(
manifestDir: manifestDir,
);
await verifyBinaries.run();
}
}
Future<void> runMain(List<String> 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);
}
}

View File

@ -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<String> 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<Map<String, String>> _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();
}
}
}

View File

@ -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);
}
}

View File

@ -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<File> files) {
final output = AccumulatorSink<Digest>();
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<File> files) {
final output = AccumulatorSink<Digest>();
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<File> getFiles() {
final src = Directory(path.join(manifestDir, 'src'));
final files = src
.listSync(recursive: true, followLinks: false)
.whereType<File>()
.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;
}

View File

@ -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<String> 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<String> 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;
}
}
}

View File

@ -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;
}

View File

@ -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<String> 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<String> 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<YamlNode> 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<dynamic, YamlNode> 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<dynamic, YamlNode> 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<BuildConfiguration, CargoBuildOptions> 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 = <BuildConfiguration, CargoBuildOptions>{};
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;
}

View File

@ -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<Target> 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<void> 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 = <CreateReleaseAsset>[];
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<Release> _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;
}
}

View File

@ -0,0 +1,136 @@
/// 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<String> targets;
}
class Rustup {
List<String>? 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<String>? _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']);
// To list all non-custom toolchains, we need to filter out lines that
// don't start with "stable", "beta", or "nightly".
Pattern nonCustom = RegExp(r"^(stable|beta|nightly)");
final lines = res.stdout
.toString()
.split('\n')
.where((e) => e.isNotEmpty && e.startsWith(nonCustom))
.map(extractToolchainName)
.toList(growable: true);
return lines
.map(
(name) => _Toolchain(
name,
_getInstalledTargets(name),
),
)
.toList(growable: true);
}
static List<String> _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;
}
}

View File

@ -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<Target> androidTargets() {
return all
.where((element) => element.android != null)
.toList(growable: false);
}
/// Returns buildable targets on current host platform ignoring Android targets.
static List<Target> 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;
}

View File

@ -0,0 +1,172 @@
/// 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<String> 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');
}
}
class TestRunCommandArgs {
final String executable;
final List<String> arguments;
final String? workingDirectory;
final Map<String, String>? environment;
final bool includeParentEnvironment;
final bool runInShell;
final Encoding? stdoutEncoding;
final Encoding? stderrEncoding;
TestRunCommandArgs({
required this.executable,
required this.arguments,
this.workingDirectory,
this.environment,
this.includeParentEnvironment = true,
this.runInShell = false,
this.stdoutEncoding,
this.stderrEncoding,
});
}
class TestRunCommandResult {
TestRunCommandResult({
this.pid = 1,
this.exitCode = 0,
this.stdout = '',
this.stderr = '',
});
final int pid;
final int exitCode;
final String stdout;
final String stderr;
}
TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride;
ProcessResult runCommand(
String executable,
List<String> arguments, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding? stdoutEncoding = systemEncoding,
Encoding? stderrEncoding = systemEncoding,
}) {
if (testRunCommandOverride != null) {
final result = testRunCommandOverride!(TestRunCommandArgs(
executable: executable,
arguments: arguments,
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
));
return ProcessResult(
result.pid,
result.exitCode,
result.stdout,
result.stderr,
);
}
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;
}
}

View File

@ -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<void> 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');
}
}
}
}
}

View File

@ -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"

View File

@ -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

View File

@ -0,0 +1,99 @@
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}/$<CONFIG>")
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${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=$<CONFIG>"
"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 "")
execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_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()

View File

@ -0,0 +1,34 @@
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.Replace('\', '/')
# The slash is important when using Get-Item on Drive letters in pwsh.
if (-not($realPath.Contains($separator)) -and $realPath.EndsWith(':')) {
$realPath += '/'
}
$item = Get-Item $realPath
if ($item.LinkTarget) {
$realPath = $item.LinkTarget.Replace('\', '/')
}
}
$realPath
}
$path = Resolve-Symlinks -Path $args[0]
Write-Host $path

View File

@ -0,0 +1,179 @@
/// 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<String> 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
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
project.exec {
commandLine 'chmod', '+x', path
}
}
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<Project> {
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 == "com.flutter.gradle.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"
// Determine if the project is an application or library
def isApplication = plugin.project.plugins.hasPlugin('com.android.application')
def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants
variants.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 = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).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.buildType.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
}
}
}
}

View File

@ -0,0 +1,91 @@
@echo off
setlocal
setlocal ENABLEDELAYEDEXPANSION
SET BASEDIR=%~dp0
if not exist "%CARGOKIT_TOOL_TEMP_DIR%" (
mkdir "%CARGOKIT_TOOL_TEMP_DIR%"
)
cd /D "%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^<String^> 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%" (
echo Running pub get in "%cd%"
"%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%" %*
)

View File

@ -0,0 +1,99 @@
#!/usr/bin/env 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<String> args) {
build_tool.runMain(args);
}
EOF
# Create alias for `shasum` if it does not exist and `sha1sum` exists
if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then
shopt -s expand_aliases
alias shasum="sha1sum"
fi
# 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
# Rebuild the tool if it was deleted by Android Studio
if [ ! -f "bin/build_tool_runner.dill" ]; then
"$DART" compile kernel bin/build_tool_runner.dart
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

View File

@ -0,0 +1 @@
// This is an empty file to force CocoaPods to create a framework.

View File

@ -0,0 +1,45 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint rust_lib_spotube.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'rust_lib_spotube'
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_spotube',
: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_spotube.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_spotube.a',
}
end

View File

@ -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_lib_spotube")
project(${PROJECT_NAME} LANGUAGES CXX)
include("../cargokit/cmake/cargokit.cmake")
apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_spotube "")
# 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_lib_spotube_bundled_libraries
"${${PROJECT_NAME}_cargokit_lib}"
PARENT_SCOPE
)

View File

@ -0,0 +1 @@
// This is an empty file to force CocoaPods to create a framework.

View File

@ -0,0 +1,44 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint rust_lib_spotube.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'rust_lib_spotube'
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_spotube',
: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_spotube.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_spotube.a',
}
end

34
rust_builder/pubspec.yaml Normal file
View File

@ -0,0 +1,34 @@
name: rust_lib_spotube
description: "Utility to build Rust code"
version: 0.0.1
publish_to: none
environment:
sdk: '>=3.3.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: ^11.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

17
rust_builder/windows/.gitignore vendored Normal file
View File

@ -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/

View File

@ -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_lib_spotube")
project(${PROJECT_NAME} LANGUAGES CXX)
include("../cargokit/cmake/cargokit.cmake")
apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_spotube "")
# 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_lib_spotube_bundled_libraries
"${${PROJECT_NAME}_cargokit_lib}"
PARENT_SCOPE
)

View File

@ -25,6 +25,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_discord_rpc flutter_discord_rpc
metadata_god metadata_god
rust_lib_spotube
smtc_windows smtc_windows
) )