dart api work

This commit is contained in:
John Smith 2022-02-13 21:09:43 -05:00
parent cbf8f30b96
commit 125901fcd8
4 changed files with 113 additions and 85 deletions

View File

@ -30,7 +30,7 @@ class _MyAppState extends State<MyApp> {
// Platform messages may fail, so we use a try/catch PlatformException. // Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null. // We also handle the message potentially returning null.
try { try {
veilidVersion = await Veilid.api.veilidVersionString(); veilidVersion = Veilid.instance.veilidVersionString();
} on PlatformException { } on PlatformException {
veilidVersion = 'Failed to get veilid version.'; veilidVersion = 'Failed to get veilid version.';
} }

View File

@ -21,30 +21,29 @@ late final _dylib =
Platform.isIOS ? DynamicLibrary.process() : DynamicLibrary.open(_path); Platform.isIOS ? DynamicLibrary.process() : DynamicLibrary.open(_path);
// Linkage for initialization // Linkage for initialization
typedef _dart_postCObject typedef _DartPostCObject
= NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>; = NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>;
// fn free_string(s: *mut std::os::raw::c_char) // fn free_string(s: *mut std::os::raw::c_char)
typedef _free_string_C = Void Function(Pointer<Utf8>); typedef _FreeStringC = Void Function(Pointer<Utf8>);
typedef _free_string_Dart = void Function(Pointer<Utf8>); typedef _FreeStringDart = void Function(Pointer<Utf8>);
// fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPostCObjectFnType) // fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPostCObjectFnType)
typedef _initializeVeilidFlutter_C = Void Function(Pointer<_dart_postCObject>); typedef _InitializeVeilidFlutterC = Void Function(Pointer<_DartPostCObject>);
typedef _initializeVeilidFlutter_Dart = void Function( typedef _InitializeVeilidFlutterDart = void Function(Pointer<_DartPostCObject>);
Pointer<_dart_postCObject>);
// fn startup_veilid_core(port: i64, config: FfiStr) // fn startup_veilid_core(port: i64, config: FfiStr)
typedef _startup_veilid_core_C = Void Function(Int64, Pointer<Utf8>); typedef _StartupVeilidCoreC = Void Function(Int64, Pointer<Utf8>);
typedef _startup_veilid_core_Dart = void Function(int, Pointer<Utf8>); typedef _StartupVeilidCoreDart = void Function(int, Pointer<Utf8>);
// fn get_veilid_state(port: i64) // fn get_veilid_state(port: i64)
typedef _get_veilid_state_C = Void Function(Int64); typedef _GetVeilidStateC = Void Function(Int64);
typedef _get_veilid_state_Dart = void Function(int); typedef _GetVeilidStateDart = void Function(int);
// fn change_api_log_level(port: i64, log_level: FfiStr) // fn change_api_log_level(port: i64, log_level: FfiStr)
typedef _change_api_log_level_C = Void Function(Int64, Pointer<Utf8>); typedef _ChangeApiLogLevelC = Void Function(Int64, Pointer<Utf8>);
typedef _change_api_log_level_Dart = void Function(int, Pointer<Utf8>); typedef _ChangeApiLogLevelDart = void Function(int, Pointer<Utf8>);
// fn shutdown_veilid_core(port: i64) // fn shutdown_veilid_core(port: i64)
typedef _shutdown_veilid_core_C = Void Function(Int64); typedef _ShutdownVeilidCoreC = Void Function(Int64);
typedef _shutdown_veilid_core_Dart = void Function(int); typedef _ShutdownVeilidCoreDart = void Function(int);
// fn veilid_version_string() -> *mut c_char // fn veilid_version_string() -> *mut c_char
typedef _veilid_version_string_C = Pointer<Utf8> Function(); typedef _VeilidVersionStringC = Pointer<Utf8> Function();
typedef _veilid_version_string_Dart = Pointer<Utf8> Function(); typedef _VeilidVersionStringDart = Pointer<Utf8> Function();
// fn veilid_version() -> VeilidVersion // fn veilid_version() -> VeilidVersion
class VeilidVersionFFI extends Struct { class VeilidVersionFFI extends Struct {
@ -56,42 +55,41 @@ class VeilidVersionFFI extends Struct {
external int patch; external int patch;
} }
typedef _veilid_version_C = VeilidVersionFFI Function(); typedef _VeilidVersionC = VeilidVersionFFI Function();
typedef _veilid_version_Dart = VeilidVersionFFI Function(); typedef _VeilidVersionDart = VeilidVersionFFI Function();
// Async message types // Async message types
const int MESSAGE_OK = 0; const int messageOk = 0;
const int MESSAGE_ERR = 1; const int messageErr = 1;
const int MESSAGE_OK_JSON = 2; const int messageOkJson = 2;
const int MESSAGE_ERR_JSON = 3; const int messageErrJson = 3;
const int MESSAGE_STREAM_ITEM = 4; const int messageStreamItem = 4;
const int MESSAGE_STREAM_ITEM_JSON = 5; const int messageStreamItemJson = 5;
const int MESSAGE_STREAM_ABORT = 6; const int messageStreamAbort = 6;
const int MESSAGE_STREAM_ABORT_JSON = 7; const int messageStreamAbortJson = 7;
const int MESSAGE_STREAM_CLOSE = 8; const int messageStreamClose = 8;
// Interface factory for high level Veilid API // Interface factory for high level Veilid API
Veilid getVeilid() => VeilidFFI(_dylib); Veilid getVeilid() => VeilidFFI(_dylib);
// Parse handle async returns // Parse handle async returns
Future<T> processFuturePlain<T>( Future<T> processFuturePlain<T>(
T Function(Map<String, dynamic>)? jsonConstructor, T Function(Map<String, dynamic>)? jsonConstructor, Future<dynamic> future) {
Future<dynamic> future) {
return future.then((value) { return future.then((value) {
final list = value as List<dynamic>; final list = value as List<dynamic>;
switch (list[0] as int) { switch (list[0] as int) {
case MESSAGE_OK: case messageOk:
{ {
if (list[1] == null) { if (list[1] == null) {
throw VeilidAPIExceptionInternal("Null MESSAGE_OK value"); throw VeilidAPIExceptionInternal("Null MESSAGE_OK value");
} }
return list[1] as T; return list[1] as T;
} }
case MESSAGE_ERR: case messageErr:
{ {
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}"); throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
} }
case MESSAGE_ERR_JSON: case messageErrJson:
{ {
throw VeilidAPIException.fromJson(jsonDecode(list[1])); throw VeilidAPIException.fromJson(jsonDecode(list[1]));
} }
@ -111,16 +109,15 @@ Future<T> processFuturePlain<T>(
} }
Future<T> processFutureJson<T>( Future<T> processFutureJson<T>(
T Function(Map<String, dynamic>) jsonConstructor, T Function(Map<String, dynamic>) jsonConstructor, Future<dynamic> future) {
Future<dynamic> future) {
return future.then((value) { return future.then((value) {
final list = value as List<dynamic>; final list = value as List<dynamic>;
switch (list[0] as int) { switch (list[0] as int) {
case MESSAGE_ERR: case messageErr:
{ {
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}"); throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
} }
case MESSAGE_OK_JSON: case messageOkJson:
{ {
if (list[1] == null) { if (list[1] == null) {
throw VeilidAPIExceptionInternal("Null MESSAGE_OK_JSON value"); throw VeilidAPIExceptionInternal("Null MESSAGE_OK_JSON value");
@ -128,7 +125,7 @@ Future<T> processFutureJson<T>(
var ret = jsonDecode(list[1] as String); var ret = jsonDecode(list[1] as String);
return jsonConstructor(ret); return jsonConstructor(ret);
} }
case MESSAGE_ERR_JSON: case messageErrJson:
{ {
throw VeilidAPIException.fromJson(jsonDecode(list[1])); throw VeilidAPIException.fromJson(jsonDecode(list[1]));
} }
@ -151,7 +148,7 @@ Future<void> processFutureVoid(Future<dynamic> future) {
return future.then((value) { return future.then((value) {
final list = value as List<dynamic>; final list = value as List<dynamic>;
switch (list[0] as int) { switch (list[0] as int) {
case MESSAGE_OK: case messageOk:
{ {
if (list[1] != null) { if (list[1] != null) {
throw VeilidAPIExceptionInternal( throw VeilidAPIExceptionInternal(
@ -159,11 +156,11 @@ Future<void> processFutureVoid(Future<dynamic> future) {
} }
return; return;
} }
case MESSAGE_ERR: case messageErr:
{ {
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}"); throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
} }
case MESSAGE_OK_JSON: case messageOkJson:
{ {
var ret = jsonDecode(list[1] as String); var ret = jsonDecode(list[1] as String);
if (ret != null) { if (ret != null) {
@ -172,7 +169,7 @@ Future<void> processFutureVoid(Future<dynamic> future) {
} }
return; return;
} }
case MESSAGE_ERR_JSON: case messageErrJson:
{ {
throw VeilidAPIException.fromJson(jsonDecode(list[1] as String)); throw VeilidAPIException.fromJson(jsonDecode(list[1] as String));
} }
@ -191,53 +188,93 @@ Future<void> processFutureVoid(Future<dynamic> future) {
}); });
} }
Stream<T> processStreamJson<T>(
T Function(Map<String, dynamic>) jsonConstructor, Stream<dynamic> stream) {
return stream.map((value) {
final list = value as List<dynamic>;
switch (list[0] as int) {
case messageErr:
{
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
}
case messageOkJson:
{
if (list[1] == null) {
throw VeilidAPIExceptionInternal("Null MESSAGE_OK_JSON value");
}
var ret = jsonDecode(list[1] as String);
return jsonConstructor(ret);
}
case messageErrJson:
{
throw VeilidAPIException.fromJson(jsonDecode(list[1]));
}
default:
{
throw VeilidAPIExceptionInternal(
"Unexpected async return message type: ${list[0]}");
}
}
}).handleError((e) {
// Wrap all other errors in VeilidAPIExceptionInternal
throw VeilidAPIExceptionInternal(e.toString());
}, test: (e) {
// Pass errors that are already VeilidAPIException through without wrapping
return e is! VeilidAPIException;
});
}
// FFI implementation of high level Veilid API // FFI implementation of high level Veilid API
class VeilidFFI implements Veilid { class VeilidFFI implements Veilid {
// veilid_core shared library // veilid_core shared library
final DynamicLibrary _dylib; final DynamicLibrary _dylib;
// Shared library functions // Shared library functions
final _free_string_Dart _freeString; final _FreeStringDart _freeString;
final _startup_veilid_core_Dart _startupVeilidCore; final _StartupVeilidCoreDart _startupVeilidCore;
final _get_veilid_state_Dart _getVeilidState; final _GetVeilidStateDart _getVeilidState;
final _change_api_log_level_Dart _changeApiLogLevel; final _ChangeApiLogLevelDart _changeApiLogLevel;
final _shutdown_veilid_core_Dart _shutdownVeilidCore; final _ShutdownVeilidCoreDart _shutdownVeilidCore;
final _veilid_version_string_Dart _veilidVersionString; final _VeilidVersionStringDart _veilidVersionString;
final _veilid_version_Dart _veilidVersion; final _VeilidVersionDart _veilidVersion;
VeilidFFI(DynamicLibrary dylib) VeilidFFI(DynamicLibrary dylib)
: _dylib = dylib, : _dylib = dylib,
_freeString = dylib _freeString =
.lookupFunction<_free_string_C, _free_string_Dart>('free_string'), dylib.lookupFunction<_FreeStringC, _FreeStringDart>('free_string'),
_startupVeilidCore = dylib.lookupFunction<_startup_veilid_core_C, _startupVeilidCore =
_startup_veilid_core_Dart>('startup_veilid_core'), dylib.lookupFunction<_StartupVeilidCoreC, _StartupVeilidCoreDart>(
'startup_veilid_core'),
_getVeilidState = _getVeilidState =
dylib.lookupFunction<_get_veilid_state_C, _get_veilid_state_Dart>( dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
'get_veilid_state'), 'get_veilid_state'),
_changeApiLogLevel = dylib.lookupFunction<_change_api_log_level_C, _changeApiLogLevel =
_change_api_log_level_Dart>('change_api_log_level'), dylib.lookupFunction<_ChangeApiLogLevelC, _ChangeApiLogLevelDart>(
_shutdownVeilidCore = dylib.lookupFunction<_shutdown_veilid_core_C, 'change_api_log_level'),
_shutdown_veilid_core_Dart>('shutdown_veilid_core'), _shutdownVeilidCore =
_veilidVersionString = dylib.lookupFunction<_veilid_version_string_C, dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
_veilid_version_string_Dart>('veilid_version_string'), 'shutdown_veilid_core'),
_veilidVersionString = dylib.lookupFunction<_VeilidVersionStringC,
_VeilidVersionStringDart>('veilid_version_string'),
_veilidVersion = _veilidVersion =
dylib.lookupFunction<_veilid_version_C, _veilid_version_Dart>( dylib.lookupFunction<_VeilidVersionC, _VeilidVersionDart>(
'veilid_version') { 'veilid_version') {
// Get veilid_flutter initializer // Get veilid_flutter initializer
var initializeVeilidFlutter = _dylib.lookupFunction< var initializeVeilidFlutter = _dylib.lookupFunction<
_initializeVeilidFlutter_C, _InitializeVeilidFlutterC,
_initializeVeilidFlutter_Dart>('initialize_veilid_flutter'); _InitializeVeilidFlutterDart>('initialize_veilid_flutter');
initializeVeilidFlutter(NativeApi.postCObject); initializeVeilidFlutter(NativeApi.postCObject);
} }
@override @override
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) { Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) {
var nativeConfig = jsonEncode(config.json, toEncodable: veilidApiToEncodable).toNativeUtf8(); var nativeConfig =
jsonEncode(config.json, toEncodable: veilidApiToEncodable)
.toNativeUtf8();
final recvPort = ReceivePort("startup_veilid_core"); final recvPort = ReceivePort("startup_veilid_core");
final sendPort = recvPort.sendPort; final sendPort = recvPort.sendPort;
_startupVeilidCore(sendPort.nativePort, nativeConfig); _startupVeilidCore(sendPort.nativePort, nativeConfig);
malloc.free(nativeConfig); malloc.free(nativeConfig);
xxx
return processStreamJson(VeilidUpdate.fromJson, recvPort); return processStreamJson(VeilidUpdate.fromJson, recvPort);
} }
@ -245,7 +282,7 @@ xxx
Future<VeilidState> getVeilidState() async { Future<VeilidState> getVeilidState() async {
final recvPort = ReceivePort("get_veilid_state"); final recvPort = ReceivePort("get_veilid_state");
final sendPort = recvPort.sendPort; final sendPort = recvPort.sendPort;
_shutdownVeilidCore(sendPort.nativePort); _getVeilidState(sendPort.nativePort);
return processFutureJson(VeilidState.fromJson, recvPort.single); return processFutureJson(VeilidState.fromJson, recvPort.single);
} }

View File

@ -4,15 +4,12 @@ import 'dart:js';
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
Veilid getVeilid() => VeilidJS(); Veilid getVeilid() => VeilidJS();
class VeilidJS { class VeilidJS implements Veilid {
Stream<VeilidUpdate> startupVeilidCore(Object? configCallback(String key)) { Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) {
throw UnimplementedError(); throw UnimplementedError();
} }
@ -20,7 +17,7 @@ class VeilidJS {
throw UnimplementedError(); throw UnimplementedError();
} }
Future<void> changeApiLogLevel(VeilidLogLevel logLevel) { Future<void> changeApiLogLevel(VeilidConfigLogLevel logLevel) {
throw UnimplementedError(); throw UnimplementedError();
} }
@ -28,11 +25,11 @@ class VeilidJS {
throw UnimplementedError(); throw UnimplementedError();
} }
Future<String> veilidVersionString() { String veilidVersionString() {
throw UnimplementedError(); throw UnimplementedError();
} }
Future<VeilidVersion> veilidVersion() { VeilidVersion veilidVersion() {
throw UnimplementedError(); throw UnimplementedError();
} }
} }

View File

@ -3,21 +3,15 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:veilid/veilid.dart'; import 'package:veilid/veilid.dart';
void main() { void main() {
const MethodChannel channel = MethodChannel('veilid'); Veilid api = Veilid.instance;
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
setUp(() { setUp(() {});
channel.setMockMethodCallHandler((MethodCall methodCall) async {
return '42';
});
});
tearDown(() { tearDown(() {});
channel.setMockMethodCallHandler(null);
});
test('getPlatformVersion', () async { test('veilidVersionString', () async {
expect(await Veilid.platformVersion, '42'); expect(Veilid.instance.veilidVersionString(), '0.1.0');
}); });
} }