From f0d7c9baf3186e77eb867a733c59fb0d6c43e341 Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 28 Dec 2022 22:53:58 -0500 Subject: [PATCH] ffi/js flutter for tabledb --- veilid-flutter/lib/veilid.dart | 110 +++++----- veilid-flutter/lib/veilid_ffi.dart | 314 ++++++++++++++++++++++++++-- veilid-flutter/lib/veilid_js.dart | 143 ++++++++++++- veilid-flutter/rust/src/dart_ffi.rs | 24 +-- veilid-wasm/src/lib.rs | 253 ++++++++++++++++++++++ 5 files changed, 764 insertions(+), 80 deletions(-) diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 2bbac86a..955dc1d7 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -31,7 +31,7 @@ class VeilidFFIConfigLoggingTerminal { }; } - VeilidFFIConfigLoggingTerminal.fromJson(Map json) + VeilidFFIConfigLoggingTerminal.fromJson(dynamic json) : enabled = json['enabled'], level = veilidConfigLogLevelFromJson(json['level']); } @@ -58,7 +58,7 @@ class VeilidFFIConfigLoggingOtlp { }; } - VeilidFFIConfigLoggingOtlp.fromJson(Map json) + VeilidFFIConfigLoggingOtlp.fromJson(dynamic json) : enabled = json['enabled'], level = veilidConfigLogLevelFromJson(json['level']), grpcEndpoint = json['grpc_endpoint'], @@ -81,7 +81,7 @@ class VeilidFFIConfigLoggingApi { }; } - VeilidFFIConfigLoggingApi.fromJson(Map json) + VeilidFFIConfigLoggingApi.fromJson(dynamic json) : enabled = json['enabled'], level = veilidConfigLogLevelFromJson(json['level']); } @@ -102,7 +102,7 @@ class VeilidFFIConfigLogging { }; } - VeilidFFIConfigLogging.fromJson(Map json) + VeilidFFIConfigLogging.fromJson(dynamic json) : terminal = VeilidFFIConfigLoggingTerminal.fromJson(json['terminal']), otlp = VeilidFFIConfigLoggingOtlp.fromJson(json['otlp']), api = VeilidFFIConfigLoggingApi.fromJson(json['api']); @@ -150,7 +150,7 @@ class VeilidWASMConfigLoggingPerformance { }; } - VeilidWASMConfigLoggingPerformance.fromJson(Map json) + VeilidWASMConfigLoggingPerformance.fromJson(dynamic json) : enabled = json['enabled'], level = veilidConfigLogLevelFromJson(json['level']), logsInTimings = json['logs_in_timings'], @@ -173,7 +173,7 @@ class VeilidWASMConfigLoggingApi { }; } - VeilidWASMConfigLoggingApi.fromJson(Map json) + VeilidWASMConfigLoggingApi.fromJson(dynamic json) : enabled = json['enabled'], level = veilidConfigLogLevelFromJson(json['level']); } @@ -191,7 +191,7 @@ class VeilidWASMConfigLogging { }; } - VeilidWASMConfigLogging.fromJson(Map json) + VeilidWASMConfigLogging.fromJson(dynamic json) : performance = VeilidWASMConfigLoggingPerformance.fromJson(json['performance']), api = VeilidWASMConfigLoggingApi.fromJson(json['api']); @@ -210,7 +210,7 @@ class VeilidWASMConfig { }; } - VeilidWASMConfig.fromJson(Map json) + VeilidWASMConfig.fromJson(dynamic json) : logging = VeilidWASMConfigLogging.fromJson(json['logging']); } @@ -324,7 +324,7 @@ class VeilidConfigHTTPS { }; } - VeilidConfigHTTPS.fromJson(Map json) + VeilidConfigHTTPS.fromJson(dynamic json) : enabled = json['enabled'], listenAddress = json['listen_address'], path = json['path'], @@ -355,7 +355,7 @@ class VeilidConfigHTTP { }; } - VeilidConfigHTTP.fromJson(Map json) + VeilidConfigHTTP.fromJson(dynamic json) : enabled = json['enabled'], listenAddress = json['listen_address'], path = json['path'], @@ -380,7 +380,7 @@ class VeilidConfigApplication { }; } - VeilidConfigApplication.fromJson(Map json) + VeilidConfigApplication.fromJson(dynamic json) : https = VeilidConfigHTTPS.fromJson(json['https']), http = VeilidConfigHTTP.fromJson(json['http']); } @@ -408,7 +408,7 @@ class VeilidConfigUDP { }; } - VeilidConfigUDP.fromJson(Map json) + VeilidConfigUDP.fromJson(dynamic json) : enabled = json['enabled'], socketPoolSize = json['socket_pool_size'], listenAddress = json['listen_address'], @@ -441,7 +441,7 @@ class VeilidConfigTCP { }; } - VeilidConfigTCP.fromJson(Map json) + VeilidConfigTCP.fromJson(dynamic json) : connect = json['connect'], listen = json['listen'], maxConnections = json['max_connections'], @@ -478,7 +478,7 @@ class VeilidConfigWS { }; } - VeilidConfigWS.fromJson(Map json) + VeilidConfigWS.fromJson(dynamic json) : connect = json['connect'], listen = json['listen'], maxConnections = json['max_connections'], @@ -516,7 +516,7 @@ class VeilidConfigWSS { }; } - VeilidConfigWSS.fromJson(Map json) + VeilidConfigWSS.fromJson(dynamic json) : connect = json['connect'], listen = json['listen'], maxConnections = json['max_connections'], @@ -549,7 +549,7 @@ class VeilidConfigProtocol { }; } - VeilidConfigProtocol.fromJson(Map json) + VeilidConfigProtocol.fromJson(dynamic json) : udp = VeilidConfigUDP.fromJson(json['udp']), tcp = VeilidConfigTCP.fromJson(json['tcp']), ws = VeilidConfigWS.fromJson(json['ws']), @@ -577,7 +577,7 @@ class VeilidConfigTLS { }; } - VeilidConfigTLS.fromJson(Map json) + VeilidConfigTLS.fromJson(dynamic json) : certificatePath = json['certificate_path'], privateKeyPath = json['private_key_path'], connectionInitialTimeoutMs = json['connection_initial_timeout_ms']; @@ -633,7 +633,7 @@ class VeilidConfigDHT { }; } - VeilidConfigDHT.fromJson(Map json) + VeilidConfigDHT.fromJson(dynamic json) : resolveNodeTimeoutMs = json['resolve_node_timeout_ms'], resolveNodeCount = json['resolve_node_count'], resolveNodeFanout = json['resolve_node_fanout'], @@ -682,7 +682,7 @@ class VeilidConfigRPC { }; } - VeilidConfigRPC.fromJson(Map json) + VeilidConfigRPC.fromJson(dynamic json) : concurrency = json['concurrency'], queueSize = json['queue_size'], maxTimestampBehindMs = json['max_timestamp_behind_ms'], @@ -719,7 +719,7 @@ class VeilidConfigRoutingTable { }; } - VeilidConfigRoutingTable.fromJson(Map json) + VeilidConfigRoutingTable.fromJson(dynamic json) : limitOverAttached = json['limit_over_attached'], limitFullyAttached = json['limit_fully_attached'], limitAttachedStrong = json['limit_attached_strong'], @@ -805,7 +805,7 @@ class VeilidConfigNetwork { }; } - VeilidConfigNetwork.fromJson(Map json) + VeilidConfigNetwork.fromJson(dynamic json) : connectionInitialTimeoutMs = json['connection_initial_timeout_ms'], connectionInactivityTimeoutMs = json['connection_inactivity_timeout_ms'], @@ -848,7 +848,7 @@ class VeilidConfigTableStore { return {'directory': directory, 'delete': delete}; } - VeilidConfigTableStore.fromJson(Map json) + VeilidConfigTableStore.fromJson(dynamic json) : directory = json['directory'], delete = json['delete']; } @@ -868,7 +868,7 @@ class VeilidConfigBlockStore { return {'directory': directory, 'delete': delete}; } - VeilidConfigBlockStore.fromJson(Map json) + VeilidConfigBlockStore.fromJson(dynamic json) : directory = json['directory'], delete = json['delete']; } @@ -897,7 +897,7 @@ class VeilidConfigProtectedStore { }; } - VeilidConfigProtectedStore.fromJson(Map json) + VeilidConfigProtectedStore.fromJson(dynamic json) : allowInsecureFallback = json['allow_insecure_fallback'], alwaysUseInsecureStorage = json['always_use_insecure_storage'], insecureFallbackDirectory = json['insecure_fallback_directory'], @@ -937,7 +937,7 @@ class VeilidConfigCapabilities { }; } - VeilidConfigCapabilities.fromJson(Map json) + VeilidConfigCapabilities.fromJson(dynamic json) : protocolUDP = json['protocol_udp'], protocolConnectTCP = json['protocol_connect_tcp'], protocolAcceptTCP = json['protocol_accept_tcp'], @@ -980,7 +980,7 @@ class VeilidConfig { }; } - VeilidConfig.fromJson(Map json) + VeilidConfig.fromJson(dynamic json) : programName = json['program_name'], namespace = json['namespace'], capabilities = VeilidConfigCapabilities.fromJson(json['capabilities']), @@ -1012,7 +1012,7 @@ class LatencyStats { }; } - LatencyStats.fromJson(Map json) + LatencyStats.fromJson(dynamic json) : fastest = BigInt.parse(json['fastest']), average = BigInt.parse(json['average']), slowest = BigInt.parse(json['slowest']); @@ -1042,7 +1042,7 @@ class TransferStats { }; } - TransferStats.fromJson(Map json) + TransferStats.fromJson(dynamic json) : total = BigInt.parse(json['total']), maximum = BigInt.parse(json['maximum']), average = BigInt.parse(json['average']), @@ -1067,7 +1067,7 @@ class TransferStatsDownUp { }; } - TransferStatsDownUp.fromJson(Map json) + TransferStatsDownUp.fromJson(dynamic json) : down = TransferStats.fromJson(json['down']), up = TransferStats.fromJson(json['up']); } @@ -1108,7 +1108,7 @@ class RPCStats { }; } - RPCStats.fromJson(Map json) + RPCStats.fromJson(dynamic json) : messagesSent = json['messages_sent'], messagesRcvd = json['messages_rcvd'], questionsInFlight = json['questions_in_flight'], @@ -1149,7 +1149,7 @@ class PeerStats { }; } - PeerStats.fromJson(Map json) + PeerStats.fromJson(dynamic json) : timeAdded = BigInt.parse(json['time_added']), rpcStats = RPCStats.fromJson(json['rpc_stats']), latency = json['latency'] != null @@ -1179,7 +1179,7 @@ class PeerTableData { }; } - PeerTableData.fromJson(Map json) + PeerTableData.fromJson(dynamic json) : nodeId = json['node_id'], peerAddress = PeerAddress.fromJson(json['peer_address']), peerStats = PeerStats.fromJson(json['peer_stats']); @@ -1223,7 +1223,7 @@ class PeerAddress { }; } - PeerAddress.fromJson(Map json) + PeerAddress.fromJson(dynamic json) : protocolType = protocolTypeFromJson(json['protocol_type']), socketAddress = json['socket_address']; } @@ -1232,7 +1232,7 @@ class PeerAddress { /// VeilidUpdate abstract class VeilidUpdate { - factory VeilidUpdate.fromJson(Map json) { + factory VeilidUpdate.fromJson(dynamic json) { switch (json["kind"]) { case "Log": { @@ -1406,7 +1406,7 @@ class VeilidStateAttachment { VeilidStateAttachment( this.state, this.publicInternetReady, this.localNetworkReady); - VeilidStateAttachment.fromJson(Map json) + VeilidStateAttachment.fromJson(dynamic json) : state = attachmentStateFromJson(json['state']), publicInternetReady = json['public_internet_ready'], localNetworkReady = json['local_network_ready']; @@ -1435,7 +1435,7 @@ class VeilidStateNetwork { required this.bpsUp, required this.peers}); - VeilidStateNetwork.fromJson(Map json) + VeilidStateNetwork.fromJson(dynamic json) : started = json['started'], bpsDown = BigInt.parse(json['bps_down']), bpsUp = BigInt.parse(json['bps_up']), @@ -1462,8 +1462,7 @@ class VeilidStateConfig { required this.config, }); - VeilidStateConfig.fromJson(Map json) - : config = json['config']; + VeilidStateConfig.fromJson(dynamic json) : config = json['config']; Map get json { return {'config': config}; @@ -1482,7 +1481,7 @@ class VeilidStateRoute { required this.deadRemoteRoutes, }); - VeilidStateRoute.fromJson(Map json) + VeilidStateRoute.fromJson(dynamic json) : deadRoutes = List.from(json['dead_routes'].map((j) => j)), deadRemoteRoutes = List.from(json['dead_remote_routes'].map((j) => j)); @@ -1503,7 +1502,7 @@ class VeilidState { final VeilidStateNetwork network; final VeilidStateConfig config; - VeilidState.fromJson(Map json) + VeilidState.fromJson(dynamic json) : attachment = VeilidStateAttachment.fromJson(json['attachment']), network = VeilidStateNetwork.fromJson(json['network']), config = VeilidStateConfig.fromJson(json['config']); @@ -1521,7 +1520,7 @@ class VeilidState { /// VeilidAPIException abstract class VeilidAPIException implements Exception { - factory VeilidAPIException.fromJson(Map json) { + factory VeilidAPIException.fromJson(dynamic json) { switch (json["kind"]) { case "NotInitialized": { @@ -1828,7 +1827,7 @@ class KeyBlob { KeyBlob(this.key, this.blob); - KeyBlob.fromJson(Map json) + KeyBlob.fromJson(dynamic json) : key = json['key'], blob = base64Decode(json['blob']); @@ -1853,9 +1852,13 @@ abstract class VeilidTableDBTransaction { Future commit(); Future rollback(); Future store(int col, Uint8List key, Uint8List value); - Future storeJson(int col, Uint8List key, Object? object, - {Object? Function(Object? nonEncodable) toEncodable}); Future delete(int col, Uint8List key); + + Future storeJson(int col, Uint8List key, Object? object, + {Object? Function(Object? nonEncodable)? toEncodable}) async { + return store(col, key, + utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable))); + } } abstract class VeilidTableDB { @@ -1863,12 +1866,23 @@ abstract class VeilidTableDB { List getKeys(int col); VeilidTableDBTransaction transact(); Future store(int col, Uint8List key, Uint8List value); - Future storeJson(int col, Uint8List key, Object? object, - {Object? Function(Object? nonEncodable) toEncodable}); Future load(int col, Uint8List key); - Future loadJson(int col, Uint8List key, - {Object? Function(Object? key, Object? value) reviver}); Future delete(int col, Uint8List key); + + Future storeJson(int col, Uint8List key, Object? object, + {Object? Function(Object? nonEncodable)? toEncodable}) { + return store(col, key, + utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable))); + } + + Future loadJson(int col, Uint8List key, + {Object? Function(Object? key, Object? value)? reviver}) async { + var s = await load(col, key); + if (s == null) { + return null; + } + return jsonDecode(utf8.decode(s, allowMalformed: false)); + } } ////////////////////////////////////// diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index c3ef7a4f..dc1bf13b 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -95,6 +95,55 @@ typedef _ReleasePrivateRouteDart = void Function(int, Pointer); typedef _AppCallReplyC = Void Function(Int64, Pointer, Pointer); typedef _AppCallReplyDart = void Function(int, Pointer, Pointer); +// fn open_table_db(port: i64, name: FfiStr, column_count: u32) +typedef _OpenTableDbC = Void Function(Int64, Pointer, Uint32); +typedef _OpenTableDbDart = void Function(int, Pointer, int); +// fn release_table_db(id: u32) -> i32 +typedef _ReleaseTableDbC = Int32 Function(Uint32); +typedef _ReleaseTableDbDart = int Function(int); +// fn delete_table_db(port: i64, name: FfiStr) +typedef _DeleteTableDbC = Void Function(Int64, Pointer); +typedef _DeleteTableDbDart = void Function(int, Pointer); +// fn table_db_get_column_count(id: u32) -> u32 +typedef _TableDbGetColumnCountC = Uint32 Function(Uint32); +typedef _TableDbGetColumnCountDart = int Function(int); +// fn table_db_get_keys(id: u32, col: u32) -> *mut c_char +typedef _TableDbGetKeysC = Pointer Function(Uint32, Uint32); +typedef _TableDbGetKeysDart = Pointer Function(int, int); +// fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) +typedef _TableDbStoreC = Void Function( + Int64, Uint32, Uint32, Pointer, Pointer); +typedef _TableDbStoreDart = void Function( + int, int, int, Pointer, Pointer); +// fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) +typedef _TableDbLoadC = Void Function(Int64, Uint32, Uint32, Pointer); +typedef _TableDbLoadDart = void Function(int, int, int, Pointer); +// fn table_db_delete(port: i64, id: u32, col: u32, key: FfiStr) +typedef _TableDbDeleteC = Void Function(Int64, Uint32, Uint32, Pointer); +typedef _TableDbDeleteDart = void Function(int, int, int, Pointer); +// fn table_db_transact(id: u32) -> u32 +typedef _TableDbTransactC = Uint32 Function(Uint32); +typedef _TableDbTransactDart = int Function(int); +// fn release_table_db_transaction(id: u32) -> i32 +typedef _ReleaseTableDbTransactionC = Int32 Function(Uint32); +typedef _ReleaseTableDbTransactionDart = int Function(int); +// fn table_db_transaction_commit(port: i64, id: u32) +typedef _TableDbTransactionCommitC = Void Function(Uint64, Uint32); +typedef _TableDbTransactionCommitDart = void Function(int, int); +// fn table_db_transaction_rollback(port: i64, id: u32) +typedef _TableDbTransactionRollbackC = Void Function(Uint64, Uint32); +typedef _TableDbTransactionRollbackDart = void Function(int, int); +// fn table_db_transaction_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) +typedef _TableDbTransactionStoreC = Void Function( + Int64, Uint32, Uint32, Pointer, Pointer); +typedef _TableDbTransactionStoreDart = void Function( + int, int, int, Pointer, Pointer); +// fn table_db_transaction_delete(port: i64, id: u32, col: u32, key: FfiStr) +typedef _TableDbTransactionDeleteC = Void Function( + Int64, Uint32, Uint32, Pointer); +typedef _TableDbTransactionDeleteDart = void Function( + int, int, int, Pointer); + // fn debug(port: i64, log_level: FfiStr) typedef _DebugC = Void Function(Int64, Pointer); typedef _DebugDart = void Function(int, Pointer); @@ -168,7 +217,7 @@ Future processFuturePlain(Future future) { } Future processFutureJson( - T Function(Map) jsonConstructor, Future future) { + T Function(dynamic) jsonConstructor, Future future) { return future.then((value) { final list = value as List; switch (list[0] as int) { @@ -388,8 +437,8 @@ class VeilidRoutingContextFFI implements VeilidRoutingContext { @override Future appMessage(String target, Uint8List message) async { - var nativeEncodedTarget = target.toNativeUtf8(); - var nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); + final nativeEncodedTarget = target.toNativeUtf8(); + final nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); final recvPort = ReceivePort("routing_context_app_message"); final sendPort = recvPort.sendPort; @@ -399,6 +448,170 @@ class VeilidRoutingContextFFI implements VeilidRoutingContext { } } +class _TDBT { + final int id; + VeilidTableDBFFI tdbffi; + VeilidFFI ffi; + + _TDBT(this.id, this.tdbffi, this.ffi); +} + +// FFI implementation of VeilidTableDBTransaction +class VeilidTableDBTransactionFFI extends VeilidTableDBTransaction { + final _TDBT _tdbt; + static final Finalizer<_TDBT> _finalizer = + Finalizer((tdbt) => {tdbt.ffi._releaseTableDbTransaction(tdbt.id)}); + + VeilidTableDBTransactionFFI._(this._tdbt) { + _finalizer.attach(this, _tdbt, detach: this); + } + + @override + Future commit() { + final recvPort = ReceivePort("veilid_table_db_transaction_commit"); + final sendPort = recvPort.sendPort; + _tdbt.ffi._tableDbTransactionCommit( + sendPort.nativePort, + _tdbt.id, + ); + return processFutureVoid(recvPort.first); + } + + @override + Future rollback() { + final recvPort = ReceivePort("veilid_table_db_transaction_rollback"); + final sendPort = recvPort.sendPort; + _tdbt.ffi._tableDbTransactionRollback( + sendPort.nativePort, + _tdbt.id, + ); + return processFutureVoid(recvPort.first); + } + + @override + Future store(int col, Uint8List key, Uint8List value) { + final nativeEncodedKey = base64UrlEncode(key).toNativeUtf8(); + final nativeEncodedValue = base64UrlEncode(value).toNativeUtf8(); + + final recvPort = ReceivePort("veilid_table_db_transaction_store"); + final sendPort = recvPort.sendPort; + _tdbt.ffi._tableDbTransactionStore( + sendPort.nativePort, + _tdbt.id, + col, + nativeEncodedKey, + nativeEncodedValue, + ); + return processFutureVoid(recvPort.first); + } + + @override + Future delete(int col, Uint8List key) { + final nativeEncodedKey = base64UrlEncode(key).toNativeUtf8(); + + final recvPort = ReceivePort("veilid_table_db_transaction_delete"); + final sendPort = recvPort.sendPort; + _tdbt.ffi._tableDbTransactionDelete( + sendPort.nativePort, + _tdbt.id, + col, + nativeEncodedKey, + ); + return processFuturePlain(recvPort.first); + } +} + +class _TDB { + final int id; + VeilidFFI ffi; + _TDB(this.id, this.ffi); +} + +// FFI implementation of VeilidTableDB +class VeilidTableDBFFI extends VeilidTableDB { + final _TDB _tdb; + static final Finalizer<_TDB> _finalizer = + Finalizer((tdb) => {tdb.ffi._releaseTableDb(tdb.id)}); + + VeilidTableDBFFI._(this._tdb) { + _finalizer.attach(this, _tdb, detach: this); + } + + @override + int getColumnCount() { + return _tdb.ffi._tableDbGetColumnCount(_tdb.id); + } + + @override + List getKeys(int col) { + final s = _tdb.ffi._tableDbGetKeys(_tdb.id, col); + if (s.address == nullptr.address) { + throw VeilidAPIExceptionInternal("No db for id"); + } + String ja = s.toDartString(); + _tdb.ffi._freeString(s); + List jarr = jsonDecode(ja); + return jarr.map((e) => base64Decode(e)).toList(); + } + + @override + VeilidTableDBTransaction transact() { + final id = _tdb.ffi._tableDbTransact(_tdb.id); + return VeilidTableDBTransactionFFI._(_TDBT(id, this, _tdb.ffi)); + } + + @override + Future store(int col, Uint8List key, Uint8List value) { + final nativeEncodedKey = base64UrlEncode(key).toNativeUtf8(); + final nativeEncodedValue = base64UrlEncode(value).toNativeUtf8(); + + final recvPort = ReceivePort("veilid_table_db_store"); + final sendPort = recvPort.sendPort; + _tdb.ffi._tableDbStore( + sendPort.nativePort, + _tdb.id, + col, + nativeEncodedKey, + nativeEncodedValue, + ); + return processFutureVoid(recvPort.first); + } + + @override + Future load(int col, Uint8List key) async { + final nativeEncodedKey = base64UrlEncode(key).toNativeUtf8(); + + final recvPort = ReceivePort("veilid_table_db_load"); + final sendPort = recvPort.sendPort; + _tdb.ffi._tableDbLoad( + sendPort.nativePort, + _tdb.id, + col, + nativeEncodedKey, + ); + String? out = await processFuturePlain(recvPort.first); + if (out == null) { + return null; + } + return base64Decode(out); + } + + @override + Future delete(int col, Uint8List key) { + final nativeEncodedKey = base64UrlEncode(key).toNativeUtf8(); + + final recvPort = ReceivePort("veilid_table_db_delete"); + final sendPort = recvPort.sendPort; + _tdb.ffi._tableDbLoad( + sendPort.nativePort, + _tdb.id, + col, + nativeEncodedKey, + ); + return processFuturePlain(recvPort.first); + } +} + // FFI implementation of high level Veilid API class VeilidFFI implements Veilid { // veilid_core shared library @@ -429,6 +642,21 @@ class VeilidFFI implements Veilid { final _AppCallReplyDart _appCallReply; + final _OpenTableDbDart _openTableDb; + final _ReleaseTableDbDart _releaseTableDb; + final _DeleteTableDbDart _deleteTableDb; + final _TableDbGetColumnCountDart _tableDbGetColumnCount; + final _TableDbGetKeysDart _tableDbGetKeys; + final _TableDbStoreDart _tableDbStore; + final _TableDbLoadDart _tableDbLoad; + final _TableDbDeleteDart _tableDbDelete; + final _TableDbTransactDart _tableDbTransact; + final _ReleaseTableDbTransactionDart _releaseTableDbTransaction; + final _TableDbTransactionCommitDart _tableDbTransactionCommit; + final _TableDbTransactionRollbackDart _tableDbTransactionRollback; + final _TableDbTransactionStoreDart _tableDbTransactionStore; + final _TableDbTransactionDeleteDart _tableDbTransactionDelete; + final _DebugDart _debug; final _VeilidVersionStringDart _veilidVersionString; final _VeilidVersionDart _veilidVersion; @@ -486,6 +714,44 @@ class VeilidFFI implements Veilid { _ReleasePrivateRouteDart>('release_private_route'), _appCallReply = dylib.lookupFunction<_AppCallReplyC, _AppCallReplyDart>( 'app_call_reply'), + _openTableDb = dylib + .lookupFunction<_OpenTableDbC, _OpenTableDbDart>('open_table_db'), + _releaseTableDb = + dylib.lookupFunction<_ReleaseTableDbC, _ReleaseTableDbDart>( + 'release_table_db'), + _deleteTableDb = + dylib.lookupFunction<_DeleteTableDbC, _DeleteTableDbDart>( + 'delete_table_db'), + _tableDbGetColumnCount = dylib.lookupFunction<_TableDbGetColumnCountC, + _TableDbGetColumnCountDart>('table_db_get_column_count'), + _tableDbGetKeys = + dylib.lookupFunction<_TableDbGetKeysC, _TableDbGetKeysDart>( + 'table_db_get_keys'), + _tableDbStore = dylib.lookupFunction<_TableDbStoreC, _TableDbStoreDart>( + 'table_db_store'), + _tableDbLoad = dylib + .lookupFunction<_TableDbLoadC, _TableDbLoadDart>('table_db_load'), + _tableDbDelete = + dylib.lookupFunction<_TableDbDeleteC, _TableDbDeleteDart>( + 'table_db_delete'), + _tableDbTransact = + dylib.lookupFunction<_TableDbTransactC, _TableDbTransactDart>( + 'table_db_transact'), + _releaseTableDbTransaction = dylib.lookupFunction< + _ReleaseTableDbTransactionC, + _ReleaseTableDbTransactionDart>('release_table_db_transaction'), + _tableDbTransactionCommit = dylib.lookupFunction< + _TableDbTransactionCommitC, + _TableDbTransactionCommitDart>('table_db_transaction_commit'), + _tableDbTransactionRollback = dylib.lookupFunction< + _TableDbTransactionRollbackC, + _TableDbTransactionRollbackDart>('table_db_transaction_rollback'), + _tableDbTransactionStore = dylib.lookupFunction< + _TableDbTransactionStoreC, + _TableDbTransactionStoreDart>('table_db_transaction_store'), + _tableDbTransactionDelete = dylib.lookupFunction< + _TableDbTransactionDeleteC, + _TableDbTransactionDeleteDart>('table_db_transaction_delete'), _debug = dylib.lookupFunction<_DebugC, _DebugDart>('debug'), _veilidVersionString = dylib.lookupFunction<_VeilidVersionStringC, _VeilidVersionStringDart>('veilid_version_string'), @@ -539,7 +805,7 @@ class VeilidFFI implements Veilid { } @override - Future getVeilidState() async { + Future getVeilidState() { final recvPort = ReceivePort("get_veilid_state"); final sendPort = recvPort.sendPort; _getVeilidState(sendPort.nativePort); @@ -547,7 +813,7 @@ class VeilidFFI implements Veilid { } @override - Future attach() async { + Future attach() { final recvPort = ReceivePort("attach"); final sendPort = recvPort.sendPort; _attach(sendPort.nativePort); @@ -555,7 +821,7 @@ class VeilidFFI implements Veilid { } @override - Future detach() async { + Future detach() { final recvPort = ReceivePort("detach"); final sendPort = recvPort.sendPort; _detach(sendPort.nativePort); @@ -563,7 +829,7 @@ class VeilidFFI implements Veilid { } @override - Future shutdownVeilidCore() async { + Future shutdownVeilidCore() { final recvPort = ReceivePort("shutdown_veilid_core"); final sendPort = recvPort.sendPort; _shutdownVeilidCore(sendPort.nativePort); @@ -580,7 +846,7 @@ class VeilidFFI implements Veilid { } @override - Future newPrivateRoute() async { + Future newPrivateRoute() { final recvPort = ReceivePort("new_private_route"); final sendPort = recvPort.sendPort; _newPrivateRoute(sendPort.nativePort); @@ -599,8 +865,8 @@ class VeilidFFI implements Veilid { } @override - Future importRemotePrivateRoute(Uint8List blob) async { - var nativeEncodedBlob = base64UrlEncode(blob).toNativeUtf8(); + Future importRemotePrivateRoute(Uint8List blob) { + final nativeEncodedBlob = base64UrlEncode(blob).toNativeUtf8(); final recvPort = ReceivePort("import_remote_private_route"); final sendPort = recvPort.sendPort; @@ -609,8 +875,8 @@ class VeilidFFI implements Veilid { } @override - Future releasePrivateRoute(String key) async { - var nativeEncodedKey = key.toNativeUtf8(); + Future releasePrivateRoute(String key) { + final nativeEncodedKey = key.toNativeUtf8(); final recvPort = ReceivePort("release_private_route"); final sendPort = recvPort.sendPort; @@ -619,15 +885,33 @@ class VeilidFFI implements Veilid { } @override - Future appCallReply(String id, Uint8List message) async { - var nativeId = id.toNativeUtf8(); - var nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); + Future appCallReply(String id, Uint8List message) { + final nativeId = id.toNativeUtf8(); + final nativeEncodedMessage = base64UrlEncode(message).toNativeUtf8(); final recvPort = ReceivePort("app_call_reply"); final sendPort = recvPort.sendPort; _appCallReply(sendPort.nativePort, nativeId, nativeEncodedMessage); return processFutureVoid(recvPort.first); } + @override + Future openTableDB(String name, int columnCount) async { + final recvPort = ReceivePort("open_table_db"); + final sendPort = recvPort.sendPort; + _openTableDb(sendPort.nativePort, name.toNativeUtf8(), columnCount); + final id = await processFuturePlain(recvPort.first); + return VeilidTableDBFFI._(_TDB(id, this)); + } + + @override + Future deleteTableDB(String name) async { + final recvPort = ReceivePort("delete_table_db"); + final sendPort = recvPort.sendPort; + _deleteTableDb(sendPort.nativePort, name.toNativeUtf8()); + final deleted = await processFuturePlain(recvPort.first); + return deleted; + } + @override Future debug(String command) async { var nativeCommand = command.toNativeUtf8(); diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index d19ce2a3..de88ccf9 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -67,7 +67,7 @@ class VeilidRoutingContextJS implements VeilidRoutingContext { } @override - Future appMessage(String target, Uint8List message) async { + Future appMessage(String target, Uint8List message) { var encodedMessage = base64UrlEncode(message); return _wrapApiPromise(js_util.callMethod( @@ -75,6 +75,127 @@ class VeilidRoutingContextJS implements VeilidRoutingContext { } } +class _TDBT { + final int id; + VeilidTableDBJS tdbjs; + VeilidJS js; + + _TDBT(this.id, this.tdbjs, this.js); +} + +// JS implementation of VeilidTableDBTransaction +class VeilidTableDBTransactionJS extends VeilidTableDBTransaction { + final _TDBT _tdbt; + static final Finalizer<_TDBT> _finalizer = Finalizer((tdbt) => { + js_util.callMethod(wasm, "release_table_db_transaction", [tdbt.id]) + }); + + VeilidTableDBTransactionJS._(this._tdbt) { + _finalizer.attach(this, _tdbt, detach: this); + } + + @override + Future commit() { + return _wrapApiPromise(js_util + .callMethod(wasm, "veilid_table_db_transaction_commit", [_tdbt.id])); + } + + @override + Future rollback() { + return _wrapApiPromise(js_util + .callMethod(wasm, "veilid_table_db_transaction_rollback", [_tdbt.id])); + } + + @override + Future store(int col, Uint8List key, Uint8List value) { + final encodedKey = base64UrlEncode(key); + final encodedValue = base64UrlEncode(value); + + return _wrapApiPromise(js_util.callMethod( + wasm, + "veilid_table_db_transaction_store", + [_tdbt.id, encodedKey, encodedValue])); + } + + @override + Future delete(int col, Uint8List key) { + final encodedKey = base64UrlEncode(key); + + return _wrapApiPromise(js_util.callMethod( + wasm, "veilid_table_db_transaction_delete", [_tdbt.id, encodedKey])); + } +} + +class _TDB { + final int id; + VeilidJS js; + + _TDB(this.id, this.js); +} + +// JS implementation of VeilidTableDB +class VeilidTableDBJS extends VeilidTableDB { + final _TDB _tdb; + static final Finalizer<_TDB> _finalizer = Finalizer((tdb) => { + js_util.callMethod(wasm, "release_table_db", [tdb.id]) + }); + + VeilidTableDBJS._(this._tdb) { + _finalizer.attach(this, _tdb, detach: this); + } + + @override + int getColumnCount() { + return js_util.callMethod(wasm, "table_db_get_column_count", [_tdb.id]); + } + + @override + List getKeys(int col) { + String? s = js_util.callMethod(wasm, "table_db_get_keys", [_tdb.id, col]); + if (s == null) { + throw VeilidAPIExceptionInternal("No db for id"); + } + List jarr = jsonDecode(s); + return jarr.map((e) => base64Decode(e)).toList(); + } + + @override + VeilidTableDBTransaction transact() { + final id = js_util.callMethod(wasm, "table_db_transact", [_tdb.id]); + + return VeilidTableDBTransactionJS._(_TDBT(id, this, _tdb.js)); + } + + @override + Future store(int col, Uint8List key, Uint8List value) { + final encodedKey = base64UrlEncode(key); + final encodedValue = base64UrlEncode(value); + + return _wrapApiPromise(js_util.callMethod( + wasm, "veilid_table_db_store", [_tdb.id, encodedKey, encodedValue])); + } + + @override + Future load(int col, Uint8List key) async { + final encodedKey = base64UrlEncode(key); + + String? out = await _wrapApiPromise(js_util + .callMethod(wasm, "veilid_table_db_store", [_tdb.id, encodedKey])); + if (out == null) { + return null; + } + return base64Decode(out); + } + + @override + Future delete(int col, Uint8List key) { + final encodedKey = base64UrlEncode(key); + + return _wrapApiPromise(js_util + .callMethod(wasm, "veilid_table_db_delete", [_tdb.id, encodedKey])); + } +} + // JS implementation of high level Veilid API class VeilidJS implements Veilid { @@ -121,12 +242,12 @@ class VeilidJS implements Veilid { } @override - Future attach() async { + Future attach() { return _wrapApiPromise(js_util.callMethod(wasm, "attach", [])); } @override - Future detach() async { + Future detach() { return _wrapApiPromise(js_util.callMethod(wasm, "detach", [])); } @@ -165,14 +286,14 @@ class VeilidJS implements Veilid { } @override - Future importRemotePrivateRoute(Uint8List blob) async { + Future importRemotePrivateRoute(Uint8List blob) { var encodedBlob = base64UrlEncode(blob); return _wrapApiPromise( js_util.callMethod(wasm, "import_remote_private_route", [encodedBlob])); } @override - Future releasePrivateRoute(String key) async { + Future releasePrivateRoute(String key) { return _wrapApiPromise( js_util.callMethod(wasm, "release_private_route", [key])); } @@ -184,6 +305,18 @@ class VeilidJS implements Veilid { js_util.callMethod(wasm, "app_call_reply", [id, encodedMessage])); } + @override + Future openTableDB(String name, int columnCount) async { + int id = await _wrapApiPromise( + js_util.callMethod(wasm, "open_table_db", [name, columnCount])); + return VeilidTableDBJS._(_TDB(id, this)); + } + + @override + Future deleteTableDB(String name) { + return _wrapApiPromise(js_util.callMethod(wasm, "delete_table_db", [name])); + } + @override Future debug(String command) async { return jsonDecode( diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 0e90ae45..695e283b 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -409,7 +409,7 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); let request: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(request.into_opt_string()) + request.into_opt_string() .unwrap() .as_bytes(), ) @@ -445,7 +445,7 @@ pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr veilid_core::deserialize_opt_json(target.into_opt_string()).unwrap(); let message: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(message.into_opt_string()) + message.into_opt_string() .unwrap() .as_bytes(), ) @@ -690,14 +690,14 @@ pub extern "C" fn table_db_transaction_rollback(port: i64, id: u32) { pub extern "C" fn table_db_transaction_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(key.into_opt_string()) + key.into_opt_string() .unwrap() .as_bytes(), ) .unwrap(); let value: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(value.into_opt_string()) + value.into_opt_string() .unwrap() .as_bytes(), ) @@ -721,7 +721,7 @@ pub extern "C" fn table_db_transaction_store(port: i64, id: u32, col: u32, key: pub extern "C" fn table_db_transaction_delete(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(key.into_opt_string()) + key.into_opt_string() .unwrap() .as_bytes(), ) @@ -744,14 +744,14 @@ pub extern "C" fn table_db_transaction_delete(port: i64, id: u32, col: u32, key: pub extern "C" fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(key.into_opt_string()) + key.into_opt_string() .unwrap() .as_bytes(), ) .unwrap(); let value: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(value.into_opt_string()) + value.into_opt_string() .unwrap() .as_bytes(), ) @@ -773,13 +773,12 @@ pub extern "C" fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, valu #[no_mangle] pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode( - veilid_core::deserialize_opt_json::(key.into_opt_string()) + .decode(key.into_opt_string() .unwrap() - .as_bytes(), + .as_bytes() ) .unwrap(); - DartIsolateWrapper::new(port).spawn_result_json(async move { + DartIsolateWrapper::new(port).spawn_result(async move { let table_db = { let table_dbs = TABLE_DBS.lock(); let Some(table_db) = table_dbs.get(&id) else { @@ -789,6 +788,7 @@ pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) { }; let out = table_db.load(col, &key).map_err(veilid_core::VeilidAPIError::generic)?; + let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); APIResult::Ok(out) }); } @@ -797,7 +797,7 @@ pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) { pub extern "C" fn table_db_delete(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD .decode( - veilid_core::deserialize_opt_json::(key.into_opt_string()) + key.into_opt_string() .unwrap() .as_bytes(), ) diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index bb0fc72f..dc85edd8 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -37,6 +37,10 @@ lazy_static! { SendWrapper::new(RefCell::new(BTreeMap::new())); static ref ROUTING_CONTEXTS: SendWrapper>> = SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref TABLE_DBS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref TABLE_DB_TRANSACTIONS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); } fn get_veilid_api() -> Result { @@ -468,6 +472,255 @@ pub fn app_call_reply(id: String, message: String) -> Promise { }) } +fn add_table_db(table_db: veilid_core::TableDB) -> u32 { + let mut next_id: u32 = 1; + let mut tdbs = (*TABLE_DBS).borrow_mut(); + while tdbs.contains_key(&next_id) { + next_id += 1; + } + tdbs.insert(next_id, table_db); + next_id +} + +#[wasm_bindgen()] +pub fn open_table_db(name: String, column_count: u32) -> Promise { + wrap_api_future(async move { + let veilid_api = get_veilid_api()?; + let tstore = veilid_api.table_store()?; + let table_db = tstore + .open(&name, column_count) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + let new_id = add_table_db(table_db); + Ok(new_id) + }) +} + +#[wasm_bindgen()] +pub fn release_table_db(id: u32) -> i32 { + let mut tdbs = (*TABLE_DBS).borrow_mut(); + if tdbs.remove(&id).is_none() { + return 0; + } + return 1; +} + +#[wasm_bindgen()] +pub fn delete_table_db(name: String) -> Promise { + wrap_api_future(async move { + let veilid_api = get_veilid_api()?; + let tstore = veilid_api.table_store()?; + let deleted = tstore + .delete(&name) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + Ok(deleted) + }) +} + +#[wasm_bindgen()] +pub fn table_db_get_column_count(id: u32) -> u32 { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return 0; + }; + let Ok(cc) = table_db.clone().get_column_count() else { + return 0; + }; + return cc; +} + +#[wasm_bindgen()] +pub fn table_db_get_keys(id: u32, col: u32) -> Option { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return None; + }; + let Ok(keys) = table_db.clone().get_keys(col) else { + return None; + }; + let keys: Vec = keys + .into_iter() + .map(|k| data_encoding::BASE64URL_NOPAD.encode(&k)) + .collect(); + let out = veilid_core::serialize_json(keys); + Some(out) +} + +fn add_table_db_transaction(tdbt: veilid_core::TableDBTransaction) -> u32 { + let mut next_id: u32 = 1; + let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); + while tdbts.contains_key(&next_id) { + next_id += 1; + } + tdbts.insert(next_id, tdbt); + next_id +} + +#[wasm_bindgen()] +pub fn table_db_transact(id: u32) -> u32 { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return 0; + }; + let tdbt = table_db.clone().transact(); + let tdbtid = add_table_db_transaction(tdbt); + return tdbtid; +} + +#[wasm_bindgen()] +pub fn release_table_db_transaction(id: u32) -> i32 { + let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); + if tdbts.remove(&id).is_none() { + return 0; + } + return 1; +} + +#[wasm_bindgen()] +pub fn table_db_transaction_commit(id: u32) -> Promise { + wrap_api_future(async move { + let tdbt = { + let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); + let Some(tdbt) = tdbts.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_commit", "id", id)); + }; + tdbt.clone() + }; + + tdbt.commit() + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_rollback(id: u32) -> Promise { + wrap_api_future(async move { + let tdbt = { + let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); + let Some(tdbt) = tdbts.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_rollback", "id", id)); + }; + tdbt.clone() + }; + + tdbt.rollback(); + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_store(id: u32, col: u32, key: String, value: String) -> Promise { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .unwrap(); + let value: Vec = data_encoding::BASE64URL_NOPAD + .decode(value.as_bytes()) + .unwrap(); + wrap_api_future(async move { + let tdbt = { + let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); + let Some(tdbt) = tdbts.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_store", "id", id)); + }; + tdbt.clone() + }; + + tdbt.store(col, &key, &value); + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_delete(id: u32, col: u32, key: String) -> Promise { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .unwrap(); + wrap_api_future(async move { + let tdbt = { + let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); + let Some(tdbt) = tdbts.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_delete", "id", id)); + }; + tdbt.clone() + }; + + let out = tdbt.delete(col, &key); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn table_db_store(id: u32, col: u32, key: String, value: String) -> Promise { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .unwrap(); + let value: Vec = data_encoding::BASE64URL_NOPAD + .decode(value.as_bytes()) + .unwrap(); + wrap_api_future(async move { + let table_db = { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_store", "id", id)); + }; + table_db.clone() + }; + + table_db + .store(col, &key, &value) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_load(id: u32, col: u32, key: String) -> Promise { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .unwrap(); + wrap_api_future(async move { + let table_db = { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_load", "id", id)); + }; + table_db.clone() + }; + + let out = table_db + .load(col, &key) + .map_err(veilid_core::VeilidAPIError::generic)?; + let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn table_db_delete(id: u32, col: u32, key: String) -> Promise { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .unwrap(); + wrap_api_future(async move { + let table_db = { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_delete", "id", id)); + }; + table_db.clone() + }; + + let out = table_db + .delete(col, &key) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + APIResult::Ok(out) + }) +} + #[wasm_bindgen()] pub fn debug(command: String) -> Promise { wrap_api_future(async move {