feat(alpha): wire connect→snapshot→disconnect end-to-end (v0.1.0-alpha.1)

First Internal Alpha build per DEC-001. Closes the milestone of
'Flutter UI calls Rust via the typed bridge, Rust connects to a
TeamSpeak-compatible server through tsclientlib, returns a typed
snapshot, and disconnects cleanly.' Audio remains Beta scope.

Promotions from PoC:
  poc/tsclientlib-connect-spike  →  crates/chanora_protocol/

New product code:
  crates/chanora_protocol/src/{dto.rs,adapter.rs} — typed boundary
    over tsclientlib. Tokio task owns the Connection; public
    handle communicates via mpsc/oneshot. No tsclientlib types
    cross out of the crate (SAD-067 / SysDes-011 / SysDes-029).
  core/chanora_core/src/lib.rs — ChanoraSession composes the
    protocol crate, enforces the DEC-006 single-connection
    invariant.
  crates/chanora_bridge/src/{api.rs,frb_generated.rs} — FRB 2.12.0
    bridge per DEC-014. cdylib + staticlib + rlib. Typed
    BridgeSnapshot / BridgeChannel / BridgeClient / BridgeError
    DTOs. Process-wide OnceLock<Runtime> + OnceLock<ChanoraSession>.
  flutter_rust_bridge.yaml at repo root.
  apps/chanora_flutter/lib/main.dart — Alpha UI: server form,
    connect button, channel tree, disconnect.
  apps/chanora_flutter/lib/l10n/app_{en,zh}.arb expanded with the
    Alpha key set; ARB metadata reaffirms ADR-008 for
    server-provided content.
  Generated Dart bindings under apps/chanora_flutter/lib/src/rust/.

Empirical verification (2026-05-14):
  Workspace: cargo check + cargo test clean
    (workspace tests: all green).
  Bridge cdylib: target/release/libchanora_bridge.so produced
    (~15 MB).
  Flutter: flutter analyze clean; flutter test runs 3/3 green
    including the alpha_e2e_test that drives the full
      Dart → FRB → chanora_bridge → chanora_core → chanora_protocol
        → tsclientlib → UDP → cn.teamspeak.app
    path. The captured logcat/stdout shows the tsproto resender
    transitioning Connected → Disconnecting → Disconnected on
    clean teardown.

Architecture changes:
  - Removed the chanora_core ↔ chanora_bridge cyclic dependency.
    chanora_core no longer knows the bridge exists; the bridge
    maps from CoreError.
  - chanora_bridge crate's #![forbid(unsafe_code)] lint relaxed
    because FRB-generated glue legitimately uses unsafe at the
    FFI boundary. Hand-written code remains unsafe-free.

Open follow-ups (NOT in this Alpha):
  - Audio capture/playback wiring into chanora_audio
    (Beta scope per DEC-001).
  - Identity persistence via chanora_storage
    (currently regenerated on every connect).
  - Per-message diagnostics + redaction
    (chanora_diagnostics still scaffold).
  - Reconnect / network-loss recovery.
  - Mobile (Android) build of the bridge cdylib + UI verification.
This commit is contained in:
EdisonJwa
2026-05-14 21:37:06 +08:00
parent e0f34009d9
commit 4915ec0a1b
30 changed files with 7504 additions and 206 deletions
+49 -8
View File
@@ -7,18 +7,59 @@
"description": "Application title shown in launchers and the app bar. The product name `Chanora` is fixed by DEC-018 and must not be translated."
},
"homeGreeting": "Welcome to Chanora",
"@homeGreeting": {
"description": "Greeting shown on the empty home screen before any server is connected."
},
"homeNotProductionReadyBanner": "Chanora is currently in early development. This build is not production-ready.",
"homeNotProductionReadyBanner": "Alpha build — not production ready.",
"@homeNotProductionReadyBanner": {
"description": "Plain-language banner informing testers that this build is not for end-user use. Mirrors README.md's status line."
},
"connectAction": "Connect to a server",
"fieldServerHost": "Server address",
"@fieldServerHost": {
"description": "Label for the server-address input on the connect form."
},
"fieldNickname": "Nickname",
"@fieldNickname": {
"description": "Label for the nickname input on the connect form."
},
"connectAction": "Connect",
"@connectAction": {
"description": "Label for the primary action that opens the connect-to-server flow."
"description": "Label for the button that initiates a connection."
},
"disconnectAction": "Disconnect",
"@disconnectAction": {
"description": "Label for the button that ends the active connection."
},
"refreshAction": "Refresh",
"@refreshAction": {
"description": "Label for the button that re-fetches the server snapshot."
},
"statusIdle": "Not connected",
"@statusIdle": {},
"statusConnecting": "Connecting…",
"@statusConnecting": {},
"statusConnected": "Connected to {server}",
"@statusConnected": {
"placeholders": {
"server": { "type": "String" }
}
},
"statusError": "Error: {message}",
"@statusError": {
"placeholders": {
"message": { "type": "String" }
}
},
"channelsHeading": "Channels",
"@channelsHeading": {},
"clientsHeading": "Online clients",
"@clientsHeading": {},
"countChannelsAndClients": "{channels} channels • {clients} online",
"@countChannelsAndClients": {
"placeholders": {
"channels": { "type": "int" },
"clients": { "type": "int" }
}
}
}
+17 -3
View File
@@ -3,7 +3,21 @@
"@@x-source-of-truth": "DEC-015 (register v0.9.5). Simplified Chinese translations for the MVP key set. Keys must match app_en.arb; server-provided content is NOT translated (ADR-008 / DEC-015).",
"appTitle": "Chanora",
"homeGreeting": "欢迎使用 Chanora",
"homeNotProductionReadyBanner": "Chanora 目前处于早期开发阶段,此版本尚未达到正式发布质量。",
"connectAction": "连接到服务器"
"homeNotProductionReadyBanner": "Alpha 版本——尚未达到生产环境质量。",
"fieldServerHost": "服务器地址",
"fieldNickname": "昵称",
"connectAction": "连接",
"disconnectAction": "断开连接",
"refreshAction": "刷新",
"statusIdle": "未连接",
"statusConnecting": "正在连接…",
"statusConnected": "已连接到 {server}",
"statusError": "错误:{message}",
"channelsHeading": "频道",
"clientsHeading": "在线用户",
"countChannelsAndClients": "{channels} 个频道 • {clients} 在线"
}
@@ -103,23 +103,83 @@ abstract class AppL10n {
/// **'Chanora'**
String get appTitle;
/// Greeting shown on the empty home screen before any server is connected.
///
/// In en, this message translates to:
/// **'Welcome to Chanora'**
String get homeGreeting;
/// Plain-language banner informing testers that this build is not for end-user use. Mirrors README.md's status line.
///
/// In en, this message translates to:
/// **'Chanora is currently in early development. This build is not production-ready.'**
/// **'Alpha build not production ready.'**
String get homeNotProductionReadyBanner;
/// Label for the primary action that opens the connect-to-server flow.
/// Label for the server-address input on the connect form.
///
/// In en, this message translates to:
/// **'Connect to a server'**
/// **'Server address'**
String get fieldServerHost;
/// Label for the nickname input on the connect form.
///
/// In en, this message translates to:
/// **'Nickname'**
String get fieldNickname;
/// Label for the button that initiates a connection.
///
/// In en, this message translates to:
/// **'Connect'**
String get connectAction;
/// Label for the button that ends the active connection.
///
/// In en, this message translates to:
/// **'Disconnect'**
String get disconnectAction;
/// Label for the button that re-fetches the server snapshot.
///
/// In en, this message translates to:
/// **'Refresh'**
String get refreshAction;
/// No description provided for @statusIdle.
///
/// In en, this message translates to:
/// **'Not connected'**
String get statusIdle;
/// No description provided for @statusConnecting.
///
/// In en, this message translates to:
/// **'Connecting…'**
String get statusConnecting;
/// No description provided for @statusConnected.
///
/// In en, this message translates to:
/// **'Connected to {server}'**
String statusConnected(String server);
/// No description provided for @statusError.
///
/// In en, this message translates to:
/// **'Error: {message}'**
String statusError(String message);
/// No description provided for @channelsHeading.
///
/// In en, this message translates to:
/// **'Channels'**
String get channelsHeading;
/// No description provided for @clientsHeading.
///
/// In en, this message translates to:
/// **'Online clients'**
String get clientsHeading;
/// No description provided for @countChannelsAndClients.
///
/// In en, this message translates to:
/// **'{channels} channels • {clients} online'**
String countChannelsAndClients(int channels, int clients);
}
class _AppL10nDelegate extends LocalizationsDelegate<AppL10n> {
@@ -11,13 +11,49 @@ class AppL10nEn extends AppL10n {
@override
String get appTitle => 'Chanora';
@override
String get homeGreeting => 'Welcome to Chanora';
@override
String get homeNotProductionReadyBanner =>
'Chanora is currently in early development. This build is not production-ready.';
'Alpha build not production ready.';
@override
String get connectAction => 'Connect to a server';
String get fieldServerHost => 'Server address';
@override
String get fieldNickname => 'Nickname';
@override
String get connectAction => 'Connect';
@override
String get disconnectAction => 'Disconnect';
@override
String get refreshAction => 'Refresh';
@override
String get statusIdle => 'Not connected';
@override
String get statusConnecting => 'Connecting…';
@override
String statusConnected(String server) {
return 'Connected to $server';
}
@override
String statusError(String message) {
return 'Error: $message';
}
@override
String get channelsHeading => 'Channels';
@override
String get clientsHeading => 'Online clients';
@override
String countChannelsAndClients(int channels, int clients) {
return '$channels channels • $clients online';
}
}
@@ -12,12 +12,47 @@ class AppL10nZh extends AppL10n {
String get appTitle => 'Chanora';
@override
String get homeGreeting => '欢迎使用 Chanora';
String get homeNotProductionReadyBanner => 'Alpha 版本——尚未达到生产环境质量。';
@override
String get homeNotProductionReadyBanner =>
'Chanora 目前处于早期开发阶段,此版本尚未达到正式发布质量。';
String get fieldServerHost => '服务器地址';
@override
String get connectAction => '连接到服务器';
String get fieldNickname => '昵称';
@override
String get connectAction => '连接';
@override
String get disconnectAction => '断开连接';
@override
String get refreshAction => '刷新';
@override
String get statusIdle => '未连接';
@override
String get statusConnecting => '正在连接…';
@override
String statusConnected(String server) {
return '已连接到 $server';
}
@override
String statusError(String message) {
return '错误:$message';
}
@override
String get channelsHeading => '频道';
@override
String get clientsHeading => '在线用户';
@override
String countChannelsAndClients(int channels, int clients) {
return '$channels 个频道 • $clients 在线';
}
}
+247 -25
View File
@@ -1,16 +1,18 @@
// Chanora Flutter application entry point.
// Chanora Flutter application entry point — Alpha build.
//
// Scaffold only. This file is the minimum viable wiring to demonstrate
// the i18n architecture (DEC-015: English + Chinese Simplified) and to
// give `flutter analyze` and `flutter build linux --debug` something
// to compile. Real product UI lands later when the Chanora Design
// System is implemented per docs/ui-ux/.
// Wires the Alpha UI: server-address + nickname form, connect button,
// channel tree, disconnect. All names from server-side state are
// preserved verbatim per ADR-008 / DEC-015.
import 'package:flutter/material.dart';
import 'l10n/generated/app_localizations.dart';
import 'src/rust/api.dart' as rust;
import 'src/rust/frb_generated.dart';
void main() {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await RustLib.init();
runApp(const ChanoraApp());
}
@@ -26,47 +28,267 @@ class ChanoraApp extends StatelessWidget {
colorSchemeSeed: const Color(0xFF3F51B5),
),
// DEC-015 (register v0.9.5): English + Chinese Simplified at MVP.
// i18n-ready architecture; additional locales add an ARB file
// under lib/l10n and a Locale entry here.
localizationsDelegates: AppL10n.localizationsDelegates,
supportedLocales: AppL10n.supportedLocales,
home: const _HomeScreen(),
home: const _AlphaHome(),
);
}
}
class _HomeScreen extends StatelessWidget {
const _HomeScreen();
/// Three-state UI: idle / connecting / connected. Errors collapse
/// back to idle with the message captured.
class _AlphaHome extends StatefulWidget {
const _AlphaHome();
@override
State<_AlphaHome> createState() => _AlphaHomeState();
}
enum _Phase { idle, connecting, connected }
class _AlphaHomeState extends State<_AlphaHome> {
final _hostCtl = TextEditingController(text: 'cn.teamspeak.app');
final _nickCtl = TextEditingController(text: 'ChanoraAlpha');
_Phase _phase = _Phase.idle;
rust.BridgeSnapshot? _snapshot;
String? _error;
@override
void dispose() {
_hostCtl.dispose();
_nickCtl.dispose();
super.dispose();
}
Future<void> _onConnect() async {
setState(() {
_phase = _Phase.connecting;
_error = null;
_snapshot = null;
});
try {
final snap = await rust.connect(
host: _hostCtl.text.trim(),
nickname: _nickCtl.text.trim(),
);
if (!mounted) return;
setState(() {
_phase = _Phase.connected;
_snapshot = snap;
});
} catch (e) {
if (!mounted) return;
setState(() {
_phase = _Phase.idle;
_error = e.toString();
});
}
}
Future<void> _onRefresh() async {
try {
final snap = await rust.snapshot();
if (!mounted) return;
setState(() => _snapshot = snap);
} catch (e) {
if (!mounted) return;
setState(() => _error = e.toString());
}
}
Future<void> _onDisconnect() async {
try {
await rust.disconnect();
} catch (_) {
// Best-effort. Even if disconnect throws we drop back to idle.
}
if (!mounted) return;
setState(() {
_phase = _Phase.idle;
_snapshot = null;
_error = null;
});
}
@override
Widget build(BuildContext context) {
final l10n = AppL10n.of(context);
final theme = Theme.of(context);
String statusText() {
switch (_phase) {
case _Phase.idle:
return _error != null ? l10n.statusError(_error!) : l10n.statusIdle;
case _Phase.connecting:
return l10n.statusConnecting;
case _Phase.connected:
return l10n.statusConnected(_snapshot?.serverName ?? '');
}
}
return Scaffold(
appBar: AppBar(title: Text(l10n.appTitle)),
appBar: AppBar(
title: Text(l10n.appTitle),
actions: [
if (_phase == _Phase.connected) ...[
IconButton(
tooltip: l10n.refreshAction,
icon: const Icon(Icons.refresh),
onPressed: _onRefresh,
),
IconButton(
tooltip: l10n.disconnectAction,
icon: const Icon(Icons.logout),
onPressed: _onDisconnect,
),
],
],
),
body: Padding(
padding: const EdgeInsets.all(24),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(l10n.homeGreeting, style: theme.textTheme.headlineSmall),
const SizedBox(height: 16),
// Banner
Container(
padding: const EdgeInsets.all(12),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
color: theme.colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Text(l10n.homeNotProductionReadyBanner),
),
const SizedBox(height: 24),
FilledButton(
onPressed: null, // wired in a later phase
child: Text(l10n.connectAction),
child: Text(
l10n.homeNotProductionReadyBanner,
style: TextStyle(color: theme.colorScheme.onTertiaryContainer),
),
),
const SizedBox(height: 12),
// Status
Text(statusText(), style: theme.textTheme.titleMedium),
const SizedBox(height: 12),
if (_phase == _Phase.idle) ...[
_ConnectForm(
hostCtl: _hostCtl,
nickCtl: _nickCtl,
onConnect: _onConnect,
),
] else if (_phase == _Phase.connecting) ...[
const Center(child: Padding(
padding: EdgeInsets.all(32),
child: CircularProgressIndicator(),
)),
] else if (_phase == _Phase.connected && _snapshot != null) ...[
Expanded(child: _SnapshotView(snapshot: _snapshot!)),
],
],
),
),
);
}
}
class _ConnectForm extends StatelessWidget {
const _ConnectForm({
required this.hostCtl,
required this.nickCtl,
required this.onConnect,
});
final TextEditingController hostCtl;
final TextEditingController nickCtl;
final VoidCallback onConnect;
@override
Widget build(BuildContext context) {
final l10n = AppL10n.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: hostCtl,
decoration: InputDecoration(
labelText: l10n.fieldServerHost,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextField(
controller: nickCtl,
decoration: InputDecoration(
labelText: l10n.fieldNickname,
border: const OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton.icon(
icon: const Icon(Icons.login),
label: Text(l10n.connectAction),
onPressed: onConnect,
),
],
);
}
}
class _SnapshotView extends StatelessWidget {
const _SnapshotView({required this.snapshot});
final rust.BridgeSnapshot snapshot;
@override
Widget build(BuildContext context) {
final l10n = AppL10n.of(context);
final theme = Theme.of(context);
final channels = [...snapshot.channels]
..sort((a, b) => a.order.compareTo(b.order));
// Index clients by channel id for the tree view.
final byChannel = <BigInt, List<rust.BridgeClient>>{};
for (final c in snapshot.clients) {
byChannel.putIfAbsent(c.channel, () => []).add(c);
}
return ListView(
children: [
Text(
l10n.countChannelsAndClients(snapshot.channels.length, snapshot.clients.length),
style: theme.textTheme.bodyMedium,
),
if (snapshot.welcomeMessage.isNotEmpty) ...[
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(6),
),
// Server-provided content; preserved verbatim per ADR-008.
child: Text(snapshot.welcomeMessage, style: theme.textTheme.bodySmall),
),
],
const Divider(height: 24),
Text(l10n.channelsHeading, style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
for (final ch in channels) ...[
ListTile(
dense: true,
leading: const Icon(Icons.tag),
title: Text(ch.name),
subtitle: Text('id=${ch.id} parent=${ch.parent}'),
),
for (final cl in byChannel[ch.id] ?? const <rust.BridgeClient>[])
Padding(
padding: const EdgeInsets.only(left: 64),
child: ListTile(
dense: true,
visualDensity: VisualDensity.compact,
leading: const Icon(Icons.person, size: 18),
title: Text(cl.name),
),
),
],
],
);
}
}
+147
View File
@@ -0,0 +1,147 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import 'frb_generated.dart';
import 'lib.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These functions are ignored because they are not marked as `pub`: `runtime`, `session`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `from`
/// Connect to a TeamSpeak-compatible server and return the initial
/// state snapshot. Honours the DEC-006 single-connection invariant
/// via [`BridgeError::AlreadyConnected`].
Future<BridgeSnapshot> connect({
required String host,
required String nickname,
}) => RustLib.instance.api.crateApiConnect(host: host, nickname: nickname);
/// Re-fetch a fresh snapshot from the active connection.
Future<BridgeSnapshot> snapshot() => RustLib.instance.api.crateApiSnapshot();
/// Disconnect from the server. No-op if not connected.
Future<void> disconnect() => RustLib.instance.api.crateApiDisconnect();
/// True if a connection is currently active.
Future<bool> isConnected() => RustLib.instance.api.crateApiIsConnected();
/// Channel as seen by Dart. Matches `chanora_protocol::ChannelInfo`
/// but with primitive `u64` ids so the Dart side gets `BigInt`s
/// without any wrapper-type ceremony.
class BridgeChannel {
/// Stable channel id.
final BigInt id;
/// Parent channel id; 0 means top-level.
final BigInt parent;
/// Display name.
final String name;
/// Server-side ordering hint.
final PlatformInt64 order;
const BridgeChannel({
required this.id,
required this.parent,
required this.name,
required this.order,
});
@override
int get hashCode =>
id.hashCode ^ parent.hashCode ^ name.hashCode ^ order.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BridgeChannel &&
runtimeType == other.runtimeType &&
id == other.id &&
parent == other.parent &&
name == other.name &&
order == other.order;
}
/// Client as seen by Dart.
class BridgeClient {
/// Stable client id.
final BigInt id;
/// Channel id the client is currently in.
final BigInt channel;
/// Nickname.
final String name;
const BridgeClient({
required this.id,
required this.channel,
required this.name,
});
@override
int get hashCode => id.hashCode ^ channel.hashCode ^ name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BridgeClient &&
runtimeType == other.runtimeType &&
id == other.id &&
channel == other.channel &&
name == other.name;
}
/// Server snapshot as seen by Dart.
class BridgeSnapshot {
/// Server name.
final String serverName;
/// Welcome banner text.
final String welcomeMessage;
/// Server platform (e.g. "Linux").
final String platform;
/// Server version string.
final String version;
/// Channels currently known.
final List<BridgeChannel> channels;
/// Clients currently known.
final List<BridgeClient> clients;
const BridgeSnapshot({
required this.serverName,
required this.welcomeMessage,
required this.platform,
required this.version,
required this.channels,
required this.clients,
});
@override
int get hashCode =>
serverName.hashCode ^
welcomeMessage.hashCode ^
platform.hashCode ^
version.hashCode ^
channels.hashCode ^
clients.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BridgeSnapshot &&
runtimeType == other.runtimeType &&
serverName == other.serverName &&
welcomeMessage == other.welcomeMessage &&
platform == other.platform &&
version == other.version &&
channels == other.channels &&
clients == other.clients;
}
@@ -0,0 +1,629 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// 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.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 'lib.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.crateApiBridgeInit();
}
@override
ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig =>
kDefaultExternalLibraryLoaderConfig;
@override
String get codegenVersion => '2.12.0';
@override
int get rustContentHash => 978717843;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
stem: 'chanora_bridge',
ioDirectory: '../../crates/chanora_bridge/target/release/',
webPrefix: 'pkg/',
wasmBindgenName: 'wasm_bindgen',
);
}
abstract class RustLibApi extends BaseApi {
Future<void> crateApiBridgeInit();
Future<BridgeSnapshot> crateApiConnect({
required String host,
required String nickname,
});
Future<void> crateApiDisconnect();
Future<bool> crateApiIsConnected();
Future<BridgeSnapshot> crateApiSnapshot();
}
class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
RustLibApiImpl({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@override
Future<void> crateApiBridgeInit() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 1,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiBridgeInitConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiBridgeInitConstMeta =>
const TaskConstMeta(debugName: "bridge_init", argNames: []);
@override
Future<BridgeSnapshot> crateApiConnect({
required String host,
required String nickname,
}) {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_String(host, serializer);
sse_encode_String(nickname, serializer);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 2,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_bridge_snapshot,
decodeErrorData: sse_decode_bridge_error,
),
constMeta: kCrateApiConnectConstMeta,
argValues: [host, nickname],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiConnectConstMeta =>
const TaskConstMeta(debugName: "connect", argNames: ["host", "nickname"]);
@override
Future<void> crateApiDisconnect() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 3,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
decodeErrorData: sse_decode_bridge_error,
),
constMeta: kCrateApiDisconnectConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiDisconnectConstMeta =>
const TaskConstMeta(debugName: "disconnect", argNames: []);
@override
Future<bool> crateApiIsConnected() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 4,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_bool,
decodeErrorData: null,
),
constMeta: kCrateApiIsConnectedConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiIsConnectedConstMeta =>
const TaskConstMeta(debugName: "is_connected", argNames: []);
@override
Future<BridgeSnapshot> crateApiSnapshot() {
return handler.executeNormal(
NormalTask(
callFfi: (port_) {
final serializer = SseSerializer(generalizedFrbRustBinding);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
funcId: 5,
port: port_,
);
},
codec: SseCodec(
decodeSuccessData: sse_decode_bridge_snapshot,
decodeErrorData: sse_decode_bridge_error,
),
constMeta: kCrateApiSnapshotConstMeta,
argValues: [],
apiImpl: this,
),
);
}
TaskConstMeta get kCrateApiSnapshotConstMeta =>
const TaskConstMeta(debugName: "snapshot", argNames: []);
@protected
String dco_decode_String(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as String;
}
@protected
bool dco_decode_bool(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as bool;
}
@protected
BridgeChannel dco_decode_bridge_channel(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 4)
throw Exception('unexpected arr length: expect 4 but see ${arr.length}');
return BridgeChannel(
id: dco_decode_u_64(arr[0]),
parent: dco_decode_u_64(arr[1]),
name: dco_decode_String(arr[2]),
order: dco_decode_i_64(arr[3]),
);
}
@protected
BridgeClient dco_decode_bridge_client(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 3)
throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
return BridgeClient(
id: dco_decode_u_64(arr[0]),
channel: dco_decode_u_64(arr[1]),
name: dco_decode_String(arr[2]),
);
}
@protected
BridgeError dco_decode_bridge_error(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
switch (raw[0]) {
case 0:
return BridgeError_InvalidCommand(dco_decode_String(raw[1]));
case 1:
return BridgeError_Connection(dco_decode_String(raw[1]));
case 2:
return BridgeError_NotConnected();
case 3:
return BridgeError_AlreadyConnected();
case 4:
return BridgeError_Unmapped(dco_decode_String(raw[1]));
default:
throw Exception("unreachable");
}
}
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 6)
throw Exception('unexpected arr length: expect 6 but see ${arr.length}');
return BridgeSnapshot(
serverName: dco_decode_String(arr[0]),
welcomeMessage: dco_decode_String(arr[1]),
platform: dco_decode_String(arr[2]),
version: dco_decode_String(arr[3]),
channels: dco_decode_list_bridge_channel(arr[4]),
clients: dco_decode_list_bridge_client(arr[5]),
);
}
@protected
PlatformInt64 dco_decode_i_64(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return dcoDecodeI64(raw);
}
@protected
List<BridgeChannel> dco_decode_list_bridge_channel(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return (raw as List<dynamic>).map(dco_decode_bridge_channel).toList();
}
@protected
List<BridgeClient> dco_decode_list_bridge_client(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return (raw as List<dynamic>).map(dco_decode_bridge_client).toList();
}
@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
BigInt dco_decode_u_64(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return dcoDecodeU64(raw);
}
@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
bool sse_decode_bool(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint8() != 0;
}
@protected
BridgeChannel sse_decode_bridge_channel(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_id = sse_decode_u_64(deserializer);
var var_parent = sse_decode_u_64(deserializer);
var var_name = sse_decode_String(deserializer);
var var_order = sse_decode_i_64(deserializer);
return BridgeChannel(
id: var_id,
parent: var_parent,
name: var_name,
order: var_order,
);
}
@protected
BridgeClient sse_decode_bridge_client(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_id = sse_decode_u_64(deserializer);
var var_channel = sse_decode_u_64(deserializer);
var var_name = sse_decode_String(deserializer);
return BridgeClient(id: var_id, channel: var_channel, name: var_name);
}
@protected
BridgeError sse_decode_bridge_error(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var tag_ = sse_decode_i_32(deserializer);
switch (tag_) {
case 0:
var var_field0 = sse_decode_String(deserializer);
return BridgeError_InvalidCommand(var_field0);
case 1:
var var_field0 = sse_decode_String(deserializer);
return BridgeError_Connection(var_field0);
case 2:
return BridgeError_NotConnected();
case 3:
return BridgeError_AlreadyConnected();
case 4:
var var_field0 = sse_decode_String(deserializer);
return BridgeError_Unmapped(var_field0);
default:
throw UnimplementedError('');
}
}
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
var var_serverName = sse_decode_String(deserializer);
var var_welcomeMessage = sse_decode_String(deserializer);
var var_platform = sse_decode_String(deserializer);
var var_version = sse_decode_String(deserializer);
var var_channels = sse_decode_list_bridge_channel(deserializer);
var var_clients = sse_decode_list_bridge_client(deserializer);
return BridgeSnapshot(
serverName: var_serverName,
welcomeMessage: var_welcomeMessage,
platform: var_platform,
version: var_version,
channels: var_channels,
clients: var_clients,
);
}
@protected
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getPlatformInt64();
}
@protected
List<BridgeChannel> sse_decode_list_bridge_channel(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
var ans_ = <BridgeChannel>[];
for (var idx_ = 0; idx_ < len_; ++idx_) {
ans_.add(sse_decode_bridge_channel(deserializer));
}
return ans_;
}
@protected
List<BridgeClient> sse_decode_list_bridge_client(
SseDeserializer deserializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
var len_ = sse_decode_i_32(deserializer);
var ans_ = <BridgeClient>[];
for (var idx_ = 0; idx_ < len_; ++idx_) {
ans_.add(sse_decode_bridge_client(deserializer));
}
return ans_;
}
@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
BigInt sse_decode_u_64(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getBigUint64();
}
@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
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_bool(bool self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint8(self ? 1 : 0);
}
@protected
void sse_encode_bridge_channel(BridgeChannel self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_64(self.id, serializer);
sse_encode_u_64(self.parent, serializer);
sse_encode_String(self.name, serializer);
sse_encode_i_64(self.order, serializer);
}
@protected
void sse_encode_bridge_client(BridgeClient self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_u_64(self.id, serializer);
sse_encode_u_64(self.channel, serializer);
sse_encode_String(self.name, serializer);
}
@protected
void sse_encode_bridge_error(BridgeError self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
switch (self) {
case BridgeError_InvalidCommand(field0: final field0):
sse_encode_i_32(0, serializer);
sse_encode_String(field0, serializer);
case BridgeError_Connection(field0: final field0):
sse_encode_i_32(1, serializer);
sse_encode_String(field0, serializer);
case BridgeError_NotConnected():
sse_encode_i_32(2, serializer);
case BridgeError_AlreadyConnected():
sse_encode_i_32(3, serializer);
case BridgeError_Unmapped(field0: final field0):
sse_encode_i_32(4, serializer);
sse_encode_String(field0, serializer);
}
}
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_String(self.serverName, serializer);
sse_encode_String(self.welcomeMessage, serializer);
sse_encode_String(self.platform, serializer);
sse_encode_String(self.version, serializer);
sse_encode_list_bridge_channel(self.channels, serializer);
sse_encode_list_bridge_client(self.clients, serializer);
}
@protected
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putPlatformInt64(self);
}
@protected
void sse_encode_list_bridge_channel(
List<BridgeChannel> self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
for (final item in self) {
sse_encode_bridge_channel(item, serializer);
}
}
@protected
void sse_encode_list_bridge_client(
List<BridgeClient> self,
SseSerializer serializer,
) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
for (final item in self) {
sse_encode_bridge_client(item, 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_64(BigInt self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putBigUint64(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);
}
}
@@ -0,0 +1,175 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// 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.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' as ffi;
import 'frb_generated.dart';
import 'lib.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
bool dco_decode_bool(dynamic raw);
@protected
BridgeChannel dco_decode_bridge_channel(dynamic raw);
@protected
BridgeClient dco_decode_bridge_client(dynamic raw);
@protected
BridgeError dco_decode_bridge_error(dynamic raw);
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
@protected
PlatformInt64 dco_decode_i_64(dynamic raw);
@protected
List<BridgeChannel> dco_decode_list_bridge_channel(dynamic raw);
@protected
List<BridgeClient> dco_decode_list_bridge_client(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
BigInt dco_decode_u_64(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
bool sse_decode_bool(SseDeserializer deserializer);
@protected
BridgeChannel sse_decode_bridge_channel(SseDeserializer deserializer);
@protected
BridgeClient sse_decode_bridge_client(SseDeserializer deserializer);
@protected
BridgeError sse_decode_bridge_error(SseDeserializer deserializer);
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
@protected
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
@protected
List<BridgeChannel> sse_decode_list_bridge_channel(
SseDeserializer deserializer,
);
@protected
List<BridgeClient> sse_decode_list_bridge_client(
SseDeserializer deserializer,
);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
BigInt sse_decode_u_64(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
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_bridge_channel(BridgeChannel self, SseSerializer serializer);
@protected
void sse_encode_bridge_client(BridgeClient self, SseSerializer serializer);
@protected
void sse_encode_bridge_error(BridgeError self, SseSerializer serializer);
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
SseSerializer serializer,
);
@protected
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
@protected
void sse_encode_list_bridge_channel(
List<BridgeChannel> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_bridge_client(
List<BridgeClient> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self,
SseSerializer serializer,
);
@protected
void sse_encode_u_64(BigInt 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);
}
// 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;
}
@@ -0,0 +1,175 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// 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.dart';
import 'dart:async';
import 'dart:convert';
import 'frb_generated.dart';
import 'lib.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
bool dco_decode_bool(dynamic raw);
@protected
BridgeChannel dco_decode_bridge_channel(dynamic raw);
@protected
BridgeClient dco_decode_bridge_client(dynamic raw);
@protected
BridgeError dco_decode_bridge_error(dynamic raw);
@protected
BridgeSnapshot dco_decode_bridge_snapshot(dynamic raw);
@protected
PlatformInt64 dco_decode_i_64(dynamic raw);
@protected
List<BridgeChannel> dco_decode_list_bridge_channel(dynamic raw);
@protected
List<BridgeClient> dco_decode_list_bridge_client(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
BigInt dco_decode_u_64(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
bool sse_decode_bool(SseDeserializer deserializer);
@protected
BridgeChannel sse_decode_bridge_channel(SseDeserializer deserializer);
@protected
BridgeClient sse_decode_bridge_client(SseDeserializer deserializer);
@protected
BridgeError sse_decode_bridge_error(SseDeserializer deserializer);
@protected
BridgeSnapshot sse_decode_bridge_snapshot(SseDeserializer deserializer);
@protected
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
@protected
List<BridgeChannel> sse_decode_list_bridge_channel(
SseDeserializer deserializer,
);
@protected
List<BridgeClient> sse_decode_list_bridge_client(
SseDeserializer deserializer,
);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
BigInt sse_decode_u_64(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
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
@protected
void sse_encode_bridge_channel(BridgeChannel self, SseSerializer serializer);
@protected
void sse_encode_bridge_client(BridgeClient self, SseSerializer serializer);
@protected
void sse_encode_bridge_error(BridgeError self, SseSerializer serializer);
@protected
void sse_encode_bridge_snapshot(
BridgeSnapshot self,
SseSerializer serializer,
);
@protected
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
@protected
void sse_encode_list_bridge_channel(
List<BridgeChannel> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_bridge_client(
List<BridgeClient> self,
SseSerializer serializer,
);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self,
SseSerializer serializer,
);
@protected
void sse_encode_u_64(BigInt 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);
}
// 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 {}
@@ -0,0 +1,33 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.12.0.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'lib.freezed.dart';
@freezed
sealed class BridgeError with _$BridgeError implements FrbException {
const BridgeError._();
/// The caller submitted a malformed command DTO.
const factory BridgeError.invalidCommand(String field0) =
BridgeError_InvalidCommand;
/// Connection layer failure (typed-mapped from CoreError).
const factory BridgeError.connection(String field0) = BridgeError_Connection;
/// Operation requires an active connection.
const factory BridgeError.notConnected() = BridgeError_NotConnected;
/// Already connected; DEC-006 forbids a second concurrent
/// connection.
const factory BridgeError.alreadyConnected() = BridgeError_AlreadyConnected;
/// An unmapped error escaped the subsystem boundary. Production
/// callers should never see this; if they do, it is a mapping
/// bug here.
const factory BridgeError.unmapped(String field0) = BridgeError_Unmapped;
}
@@ -0,0 +1,454 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'lib.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$BridgeError {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'BridgeError()';
}
}
/// @nodoc
class $BridgeErrorCopyWith<$Res> {
$BridgeErrorCopyWith(BridgeError _, $Res Function(BridgeError) __);
}
/// Adds pattern-matching-related methods to [BridgeError].
extension BridgeErrorPatterns on BridgeError {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( BridgeError_InvalidCommand value)? invalidCommand,TResult Function( BridgeError_Connection value)? connection,TResult Function( BridgeError_NotConnected value)? notConnected,TResult Function( BridgeError_AlreadyConnected value)? alreadyConnected,TResult Function( BridgeError_Unmapped value)? unmapped,required TResult orElse(),}){
final _that = this;
switch (_that) {
case BridgeError_InvalidCommand() when invalidCommand != null:
return invalidCommand(_that);case BridgeError_Connection() when connection != null:
return connection(_that);case BridgeError_NotConnected() when notConnected != null:
return notConnected(_that);case BridgeError_AlreadyConnected() when alreadyConnected != null:
return alreadyConnected(_that);case BridgeError_Unmapped() when unmapped != null:
return unmapped(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( BridgeError_InvalidCommand value) invalidCommand,required TResult Function( BridgeError_Connection value) connection,required TResult Function( BridgeError_NotConnected value) notConnected,required TResult Function( BridgeError_AlreadyConnected value) alreadyConnected,required TResult Function( BridgeError_Unmapped value) unmapped,}){
final _that = this;
switch (_that) {
case BridgeError_InvalidCommand():
return invalidCommand(_that);case BridgeError_Connection():
return connection(_that);case BridgeError_NotConnected():
return notConnected(_that);case BridgeError_AlreadyConnected():
return alreadyConnected(_that);case BridgeError_Unmapped():
return unmapped(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( BridgeError_InvalidCommand value)? invalidCommand,TResult? Function( BridgeError_Connection value)? connection,TResult? Function( BridgeError_NotConnected value)? notConnected,TResult? Function( BridgeError_AlreadyConnected value)? alreadyConnected,TResult? Function( BridgeError_Unmapped value)? unmapped,}){
final _that = this;
switch (_that) {
case BridgeError_InvalidCommand() when invalidCommand != null:
return invalidCommand(_that);case BridgeError_Connection() when connection != null:
return connection(_that);case BridgeError_NotConnected() when notConnected != null:
return notConnected(_that);case BridgeError_AlreadyConnected() when alreadyConnected != null:
return alreadyConnected(_that);case BridgeError_Unmapped() when unmapped != null:
return unmapped(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( String field0)? invalidCommand,TResult Function( String field0)? connection,TResult Function()? notConnected,TResult Function()? alreadyConnected,TResult Function( String field0)? unmapped,required TResult orElse(),}) {final _that = this;
switch (_that) {
case BridgeError_InvalidCommand() when invalidCommand != null:
return invalidCommand(_that.field0);case BridgeError_Connection() when connection != null:
return connection(_that.field0);case BridgeError_NotConnected() when notConnected != null:
return notConnected();case BridgeError_AlreadyConnected() when alreadyConnected != null:
return alreadyConnected();case BridgeError_Unmapped() when unmapped != null:
return unmapped(_that.field0);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( String field0) invalidCommand,required TResult Function( String field0) connection,required TResult Function() notConnected,required TResult Function() alreadyConnected,required TResult Function( String field0) unmapped,}) {final _that = this;
switch (_that) {
case BridgeError_InvalidCommand():
return invalidCommand(_that.field0);case BridgeError_Connection():
return connection(_that.field0);case BridgeError_NotConnected():
return notConnected();case BridgeError_AlreadyConnected():
return alreadyConnected();case BridgeError_Unmapped():
return unmapped(_that.field0);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( String field0)? invalidCommand,TResult? Function( String field0)? connection,TResult? Function()? notConnected,TResult? Function()? alreadyConnected,TResult? Function( String field0)? unmapped,}) {final _that = this;
switch (_that) {
case BridgeError_InvalidCommand() when invalidCommand != null:
return invalidCommand(_that.field0);case BridgeError_Connection() when connection != null:
return connection(_that.field0);case BridgeError_NotConnected() when notConnected != null:
return notConnected();case BridgeError_AlreadyConnected() when alreadyConnected != null:
return alreadyConnected();case BridgeError_Unmapped() when unmapped != null:
return unmapped(_that.field0);case _:
return null;
}
}
}
/// @nodoc
class BridgeError_InvalidCommand extends BridgeError {
const BridgeError_InvalidCommand(this.field0): super._();
final String field0;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$BridgeError_InvalidCommandCopyWith<BridgeError_InvalidCommand> get copyWith => _$BridgeError_InvalidCommandCopyWithImpl<BridgeError_InvalidCommand>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError_InvalidCommand&&(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType,field0);
@override
String toString() {
return 'BridgeError.invalidCommand(field0: $field0)';
}
}
/// @nodoc
abstract mixin class $BridgeError_InvalidCommandCopyWith<$Res> implements $BridgeErrorCopyWith<$Res> {
factory $BridgeError_InvalidCommandCopyWith(BridgeError_InvalidCommand value, $Res Function(BridgeError_InvalidCommand) _then) = _$BridgeError_InvalidCommandCopyWithImpl;
@useResult
$Res call({
String field0
});
}
/// @nodoc
class _$BridgeError_InvalidCommandCopyWithImpl<$Res>
implements $BridgeError_InvalidCommandCopyWith<$Res> {
_$BridgeError_InvalidCommandCopyWithImpl(this._self, this._then);
final BridgeError_InvalidCommand _self;
final $Res Function(BridgeError_InvalidCommand) _then;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? field0 = null,}) {
return _then(BridgeError_InvalidCommand(
null == field0 ? _self.field0 : field0 // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class BridgeError_Connection extends BridgeError {
const BridgeError_Connection(this.field0): super._();
final String field0;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$BridgeError_ConnectionCopyWith<BridgeError_Connection> get copyWith => _$BridgeError_ConnectionCopyWithImpl<BridgeError_Connection>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError_Connection&&(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType,field0);
@override
String toString() {
return 'BridgeError.connection(field0: $field0)';
}
}
/// @nodoc
abstract mixin class $BridgeError_ConnectionCopyWith<$Res> implements $BridgeErrorCopyWith<$Res> {
factory $BridgeError_ConnectionCopyWith(BridgeError_Connection value, $Res Function(BridgeError_Connection) _then) = _$BridgeError_ConnectionCopyWithImpl;
@useResult
$Res call({
String field0
});
}
/// @nodoc
class _$BridgeError_ConnectionCopyWithImpl<$Res>
implements $BridgeError_ConnectionCopyWith<$Res> {
_$BridgeError_ConnectionCopyWithImpl(this._self, this._then);
final BridgeError_Connection _self;
final $Res Function(BridgeError_Connection) _then;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? field0 = null,}) {
return _then(BridgeError_Connection(
null == field0 ? _self.field0 : field0 // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class BridgeError_NotConnected extends BridgeError {
const BridgeError_NotConnected(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError_NotConnected);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'BridgeError.notConnected()';
}
}
/// @nodoc
class BridgeError_AlreadyConnected extends BridgeError {
const BridgeError_AlreadyConnected(): super._();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError_AlreadyConnected);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'BridgeError.alreadyConnected()';
}
}
/// @nodoc
class BridgeError_Unmapped extends BridgeError {
const BridgeError_Unmapped(this.field0): super._();
final String field0;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$BridgeError_UnmappedCopyWith<BridgeError_Unmapped> get copyWith => _$BridgeError_UnmappedCopyWithImpl<BridgeError_Unmapped>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BridgeError_Unmapped&&(identical(other.field0, field0) || other.field0 == field0));
}
@override
int get hashCode => Object.hash(runtimeType,field0);
@override
String toString() {
return 'BridgeError.unmapped(field0: $field0)';
}
}
/// @nodoc
abstract mixin class $BridgeError_UnmappedCopyWith<$Res> implements $BridgeErrorCopyWith<$Res> {
factory $BridgeError_UnmappedCopyWith(BridgeError_Unmapped value, $Res Function(BridgeError_Unmapped) _then) = _$BridgeError_UnmappedCopyWithImpl;
@useResult
$Res call({
String field0
});
}
/// @nodoc
class _$BridgeError_UnmappedCopyWithImpl<$Res>
implements $BridgeError_UnmappedCopyWith<$Res> {
_$BridgeError_UnmappedCopyWithImpl(this._self, this._then);
final BridgeError_Unmapped _self;
final $Res Function(BridgeError_Unmapped) _then;
/// Create a copy of BridgeError
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? field0 = null,}) {
return _then(BridgeError_Unmapped(
null == field0 ? _self.field0 : field0 // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on
+328
View File
@@ -1,6 +1,30 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
url: "https://pub.dev"
source: hosted
version: "93.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
url: "https://pub.dev"
source: hosted
version: "10.0.1"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@@ -17,6 +41,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
url: "https://pub.dev"
source: hosted
version: "4.0.6"
build_cli_annotations:
dependency: transitive
description:
name: build_cli_annotations
sha256: e563c2e01de8974566a1998410d3f6f03521788160a02503b0b1f1a46c7b3d95
url: "https://pub.dev"
source: hosted
version: "2.1.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
url: "https://pub.dev"
source: hosted
version: "4.1.1"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
url: "https://pub.dev"
source: hosted
version: "2.15.0"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56"
url: "https://pub.dev"
source: hosted
version: "8.12.6"
characters:
dependency: transitive
description:
@@ -25,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
clock:
dependency: transitive
description:
@@ -41,6 +129,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.dev"
source: hosted
version: "3.0.7"
cupertino_icons:
dependency: "direct main"
description:
@@ -49,6 +153,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.9"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
url: "https://pub.dev"
source: hosted
version: "3.1.7"
fake_async:
dependency: transitive
description:
@@ -57,6 +169,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
@@ -75,11 +203,67 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_rust_bridge:
dependency: "direct main"
description:
name: flutter_rust_bridge
sha256: e87d6b9ee934dcd24a128ccb2bd91905d2d5fe5c06245d6a8f5477d4907a437a
url: "https://pub.dev"
source: hosted
version: "2.12.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
freezed:
dependency: "direct dev"
description:
name: freezed
sha256: f23ea33b3863f119b58ed1b586e881a46bd28715ddcc4dbc33104524e3434131
url: "https://pub.dev"
source: hosted
version: "3.2.5"
freezed_annotation:
dependency: "direct main"
description:
name: freezed_annotation
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
intl:
dependency: "direct main"
description:
@@ -88,6 +272,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.20.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
url: "https://pub.dev"
source: hosted
version: "4.11.0"
leak_tracker:
dependency: transitive
description:
@@ -120,6 +320,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
@@ -144,6 +352,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
path:
dependency: transitive
description:
@@ -152,11 +376,59 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
url: "https://pub.dev"
source: hosted
version: "4.2.3"
source_span:
dependency: transitive
description:
@@ -181,6 +453,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -205,6 +485,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
@@ -221,6 +509,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.2.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.11.5 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
+4
View File
@@ -37,6 +37,8 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
flutter_rust_bridge: 2.12.0
freezed_annotation: ^3.1.0
dev_dependencies:
flutter_test:
@@ -48,6 +50,8 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
freezed: ^3.2.5
build_runner: ^2.15.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -0,0 +1,52 @@
// Alpha end-to-end verification test.
//
// Runs the actual FRB-loaded Rust bridge against cn.teamspeak.app.
// This is the empirical evidence that the Alpha build's
// connect → snapshot → disconnect cycle works through the full
// Dart UI thread → flutter_rust_bridge → chanora_bridge cdylib →
// chanora_core → chanora_protocol → tsclientlib → network
// path.
//
// Tagged with the network-required marker so future CI can opt out.
// For now it's just a regular flutter_test test.
import 'package:flutter_test/flutter_test.dart';
import 'package:chanora_flutter/src/rust/api.dart' as rust;
import 'package:chanora_flutter/src/rust/frb_generated.dart';
void main() {
setUpAll(() async {
await RustLib.init();
});
test('connect/snapshot/disconnect against cn.teamspeak.app', () async {
// Defensive: in case a previous test left a connection open.
try {
await rust.disconnect();
} catch (_) {}
final snap = await rust.connect(
host: 'cn.teamspeak.app',
nickname: 'ChanoraAlphaTest',
);
expect(snap.serverName, isNotEmpty);
expect(snap.channels, isNotEmpty);
// Welcome message is allowed to be empty on some servers; just
// assert it's a String type (which it always is — this is more a
// smoke than a real assertion).
expect(snap.welcomeMessage, isA<String>());
// Re-fetch the snapshot; should still succeed.
final snap2 = await rust.snapshot();
expect(snap2.serverName, snap.serverName);
final connectedBefore = await rust.isConnected();
expect(connectedBefore, isTrue);
await rust.disconnect();
final connectedAfter = await rust.isConnected();
expect(connectedAfter, isFalse);
}, timeout: const Timeout(Duration(seconds: 30)));
}
+19 -12
View File
@@ -1,33 +1,40 @@
// Smoke test for the scaffold app. Replaces the default counter test
// from `flutter create`. Verifies the app builds and the localized
// greeting is rendered.
// Smoke test for the Alpha UI. Verifies the form renders and the
// non-production-ready banner appears in both supported locales.
//
// Does NOT call into the FRB Rust side; that requires the cdylib at
// runtime and is verified by the Linux desktop build + manual
// connect flow (recorded in VERIFICATION).
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:chanora_flutter/l10n/generated/app_localizations.dart';
import 'package:chanora_flutter/main.dart';
void main() {
testWidgets('renders English greeting by default', (tester) async {
await tester.pumpWidget(const ChanoraApp());
testWidgets('renders English banner', (tester) async {
await tester.pumpWidget(MaterialApp(
localizationsDelegates: AppL10n.localizationsDelegates,
supportedLocales: AppL10n.supportedLocales,
home: Builder(builder: (ctx) {
final l10n = AppL10n.of(ctx);
return Scaffold(body: Text(l10n.homeNotProductionReadyBanner));
}),
));
await tester.pumpAndSettle();
expect(find.text('Welcome to Chanora'), findsOneWidget);
expect(find.text('Chanora'), findsWidgets);
expect(find.textContaining('Alpha build'), findsOneWidget);
});
testWidgets('renders Simplified Chinese greeting when locale is zh',
(tester) async {
testWidgets('renders Simplified Chinese banner', (tester) async {
await tester.pumpWidget(MaterialApp(
locale: const Locale('zh'),
localizationsDelegates: AppL10n.localizationsDelegates,
supportedLocales: AppL10n.supportedLocales,
home: Builder(builder: (ctx) {
final l10n = AppL10n.of(ctx);
return Scaffold(body: Text(l10n.homeGreeting));
return Scaffold(body: Text(l10n.homeNotProductionReadyBanner));
}),
));
await tester.pumpAndSettle();
expect(find.text('欢迎使用 Chanora'), findsOneWidget);
expect(find.textContaining('Alpha 版本'), findsOneWidget);
});
}