diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..35410cacdc5e87f985c93a96520f5e11a5c822e4
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..08c3be96e3e711e0801af36f5fae65bb157af76d
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/rust_proj_mock.iml b/.idea/rust_proj_mock.iml
new file mode 100644
index 0000000000000000000000000000000000000000..0f9a3c60115f9c8d606d87063c995759c868aee6
--- /dev/null
+++ b/.idea/rust_proj_mock.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..c85902c821c0e17b288c4e38b8b7889c60557b72
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,22 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "hilog_rust"
+version = "0.1.0"
+
+[[package]]
+name = "ipc_rust"
+version = "0.1.0"
+dependencies = [
+ "hilog_rust",
+]
+
+[[package]]
+name = "ylong_cloud_extension"
+version = "1.0.0"
+dependencies = [
+ "hilog_rust",
+ "ipc_rust",
+]
diff --git a/src/cloud_extension/BUILD.gn b/src/cloud_extension/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..bd6b46342dcf11536c9a4e8dd029ef51c6d1e1be
--- /dev/null
+++ b/src/cloud_extension/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright (c) 2023 Huawei Device Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/ohos.gni")
+import("//build/test.gni")
+
+ohos_rust_shared_ffi("ylong_cloud_extension") {
+ crate_name = "ylong_cloud_extension"
+ crate_root = "src/lib.rs"
+
+ sources = [ "src/lib.rs" ]
+ external_deps = [
+ "hilog:hilog_rust",
+ "ipc:ipc_rust"
+ ]
+ deps = [
+ "../connect_adapter:conn_adapter"
+ ]
+ subsystem_name = "distributeddatamgr"
+
+ part_name = "datamgr_service"
+}
+
+ohos_rust_unittest("rust_ylong_cloud_ext_unit_test") {
+ module_out_path = "distributeddatamgr/datamgr_service/services/rust/ylong_cloud_extension"
+ sources = [ "src/lib.rs" ]
+
+ external_deps = [
+ "hilog:hilog_rust",
+ "ipc:ipc_rust"
+ ]
+ deps = [
+ ":ylong_cloud_extension",
+ "../connect_adapter:conn_adapter"
+ ]
+}
\ No newline at end of file
diff --git a/src/cloud_extension/Cargo.toml b/src/cloud_extension/Cargo.toml
index cd717be66c2826bdd3f10df30088cf0c47ec766f..0dff70769bc216e61bb6ad0ad9b036f5d1c6b342 100644
--- a/src/cloud_extension/Cargo.toml
+++ b/src/cloud_extension/Cargo.toml
@@ -1,10 +1,23 @@
+# Copyright (c) 2023 Huawei Device Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
[package]
-name = "cloud_extension"
+name = "ylong_cloud_extension"
version = "1.0.0"
edition = "2021"
description = "Cloud and Local End Synchronization General Implementation in Rust. Belong to OH Distributed Data Service."
license = "Apache-2.0"
-repository = "https://gitee.com/openharmony-sig/commonlibrary_rust_ylong_runtime"
+repository = "https://gitee.com/openharmony-sig/"
keywords = ["cloud", "distributeddataservice"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,10 +26,6 @@ keywords = ["cloud", "distributeddataservice"]
name = "cloud_extension"
crate-type = ["cdylib", "staticlib", "lib"]
-[features]
-default = []
-c_adapter = []
-
[dependencies]
ipc_rust = {path = "../ipc_rust", version = "0.1.0"}
hilog_rust = { path = "../hilog_rust", version = "0.1.0" }
\ No newline at end of file
diff --git a/src/cloud_extension/include/basic_rust_types.h b/src/cloud_extension/include/basic_rust_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd3df8655bdaadce49af756b0e2d06de170e60a2
--- /dev/null
+++ b/src/cloud_extension/include/basic_rust_types.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H
+#define CLOUD_EXTENSION_BASIC_RUST_TYPES_H
+
+#ifndef CLOUD_EXTENSION_ERROR_H
+#include "error.h"
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif
+#endif
+
+typedef enum ValueType {
+ NULL = 0,
+ U32,
+ I32,
+ STRING,
+ VALUE,
+ VALUE_BUCKET,
+ DATABASE,
+ TABLE,
+ FIELD,
+ RELATION,
+ RELATION_SET,
+ ASSET,
+ APP_INFO,
+ VEC_U32,
+ VEC_DATABASE,
+ HASHMAP_VALUE,
+};
+
+/**
+* @brief Type declaration of Rust struct Vector.
+* @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `VectorFree` to release
+* the memory it occupies.
+*/
+typedef void Vector;
+
+/**
+ * @brief Create a Vector enum according to ValueType.
+ * @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `VectorFree` to release
+ * the memory it occupies.
+ */
+Vector *VectorNew(ValueTyp typ);
+
+/**
+ * @breif Get ValueType of the Vector pointer.
+ */
+ValueTyp VectorGetValueTyp(Vector *src);
+
+/**
+ * @breif Push value into the Vector pointer.
+ * @attention For Values pushed in, if they're created by other functions and allocated in Rust side,
+ * pushing them will transfer the management to the Vector. Therefore, no more free is needed.
+ */
+int VectorPush(
+ Vector *src,
+ void *value,
+ size_t valueLen,
+);
+
+/**
+ * @breif Get value from the Vector pointer.
+ * @attention If value type is Vector or HashMap, value will be copied so that the C side can get information stored
+ * in Rust side. In this case, the pointer returned back should be freed by VectorFree or HashMapFree.
+ */
+int VectorGet(
+ Vector *src,
+ size_t idx,
+ void **value,
+ size_t *valueLen,
+);
+
+/**
+ * @brief Get length of a Vector pointer.
+ */
+int VectorGetLength(Vector *src, size_t *len);
+
+/**
+ * @brief Free a Vector pointer.
+ */
+void VectorFree(Vector *src);
+
+/**
+* @brief Type declaration of Rust struct HashMap.
+* @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `HashMapFree` to release
+* the memory it occupies.
+*/
+typedef void HashMap;
+
+/**
+ * @brief Initialize a HashMap enum by its ValueType. The key type is fixed to be String.
+ * @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `HashMapFree` to release
+ * the memory it occupies.
+ */
+HashMap *HashMapNew(ValueTyp valueTyp);
+
+/**
+ * @brief Get key type of a Hashmap pointer.
+ */
+ValueTyp HashMapGetKeyTyp(HashMap *src);
+
+/**
+ * @brief Get value type of a Hashmap pointer.
+ */
+ValueTyp HashMapGetValueTyp(HashMap *src);
+
+/**
+ * @brief Get length of a Hashmap pointer.
+ */
+int HashMapGetLength(HashMap *src, size_t *len);
+
+/**
+ * @brief Insert key, value pairs into the Hashmap pointer.
+ * @attention For Values pushed in, if they're created by other functions and allocated in Rust side,
+ * pushing them will transfer the management to the HashMap. Therefore, no more free is needed.
+ */
+int HashMapInsert(
+ HashMap *src,
+ void *key,
+ size_t keyLen,
+ void *value,
+ size_t valueLen,
+);
+
+/**
+ * @brief Get key, value pair from the Hashmap pointer.
+ * @param src [IN]
+ * key [OUT] Vec, here fixed to be Vec now
+ * value [OUT] Vec
+ * @retval SUCCESS
+ * ERRNO_NULLPTR
+ * @attention The vectors output should be freed by VectorFree.
+ */
+int HashMapIterGetKeyValuePair(
+ HashMap *src,
+ Vector **key,
+ Vector **value,
+);
+
+/**
+ * @brief According to key, get value from the Hashmap pointer.
+ * @attention If value type is Vector or HashMap, value will be copied so that the C side can get information stored
+ * in Rust side. In this case, the pointer returned back should be freed by VectorFree or HashMapFree.
+ */
+int HashMapGet(
+ HashMap *src,
+ void *key,
+ size_t keyLen,
+ void **value,
+ size_t *valueLen,
+);
+
+/**
+ * @brief Free a Hashmap pointer.
+ */
+void HashMapFree(HashMap *src);
+
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif
+#endif
+
+#endif //CLOUD_EXTENSION_BASIC_RUST_TYPES_H
diff --git a/src/cloud_extension/include/cloud_ext_types.h b/src/cloud_extension/include/cloud_ext_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9493b6cac789ba7cda85785419f77fe6e1edcf2
--- /dev/null
+++ b/src/cloud_extension/include/cloud_ext_types.h
@@ -0,0 +1,633 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef CLOUD_EXTENSION_TYPES_H
+#define CLOUD_EXTENSION_TYPES_H
+
+#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H
+#include "basic_rust_types.h"
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif
+#endif
+
+/**
+ * @brief Type declaration of Rust cloud extension struct Value.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to
+ * release the memory occupied
+ */
+typedef void Value;
+
+/**
+ * @brief Enumeration represents inner type of contents in Value enum.
+ */
+typedef enum ValueInnerType {
+ EMPTY = 0,
+ INT = 1,
+ DOUBLE = 2,
+ BOOL = 3,
+ STRING = 4,
+ BYTES = 5,
+ ASSET = 6,
+ ASSETS = 7
+};
+
+/**
+ * @brief Create an Value instance according to ValueInnerType.
+ * @retval != NULL, a valid pointer of Value.
+ */
+Value *ValueNew(ValueInnerType typ, void *content, size_t contentLen);
+
+/**
+ * @brief Get content from a Value pointer.
+ * @param src [IN]
+ * typ [OUT] User should use this value to cast the output pointer.
+ * content [OUT] The pointer value will be updated to point to the content of the Value.
+ * contentLen [OUT]
+ * @attention If the type is Assets, the Vector pointer returned should be freed by VectorFree.
+ */
+int ValueGetContent(Value *src, ValueInnerType *typ, void **content, size_t *contentLen);
+
+/**
+ * @brief Free a Value pointer.
+ */
+void ValueFree(Value *src);
+
+/**
+ * @brief AssetStutus enumeration.
+ */
+typedef enum AssetStatus {
+ UNKNOWN = 0,
+ NORMAL,
+ INSERT,
+ UPDATE,
+ DELETE,
+ ABNORMAL,
+ DOWNLOADING,
+ BUTT,
+};
+
+/**
+* @brief Asset builder that will used to build an Asset in Rust side
+*/
+typedef struct {
+ unsigned int version;
+ AssetStatus status;
+ unsigned int expiresTime;
+ unsigned char *id;
+ size_t idLen;
+ unsigned char *name;
+ size_t nameLen;
+ unsigned char *uri;
+ size_t uriLen;
+ unsigned char *localPath;
+ size_t localPathLen;
+ unsigned char *createTime;
+ size_t createTimeLen;
+ unsigned char *modifyTime;
+ size_t modifyTimeLen;
+ unsigned char *size;
+ size_t sizeLen;
+ unsigned char *hash;
+ size_t hashLen;
+} AssetBuilder;
+
+/**
+ * @brief Type declaration of Rust cloud extension struct Value.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to
+ * release the memory occupied
+ */
+typedef void Asset;
+
+/**
+ * @brief Initialize an Asset by the AssetBuilder.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to
+ * release the memory occupied
+ */
+Asset *AssetNew(AssetBuilder *builder);
+
+/**
+ * @brief Get id from an Asset pointer.
+ */
+int AssetGetId(Asset *asset, unsigned char **id, size_t *len);
+
+/**
+ * @brief Get name from an Asset pointer.
+ */
+int AssetGetName(Asset *asset, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get uri from an Asset pointer.
+ */
+int AssetGetUri(Asset *asset, unsigned char **uri, size_t *len);
+
+/**
+ * @brief Get local path from an Asset pointer.
+ */
+int AssetGetLocalPath(Asset *asset, unsigned char **localPath, size_t *len);
+
+/**
+ * @brief Get create time from an Asset pointer.
+ */
+int AssetGetCreateTime(Asset *asset, unsigned char **createTime, size_t *len);
+
+/**
+ * @brief Get modified time from an Asset pointer.
+ */
+int AssetGetModifiedTime(Asset *asset, unsigned char **modifiedTime, size_t *len);
+
+/**
+ * @brief Get size from an Asset pointer.
+ */
+int AssetGetSize(Asset *asset, unsigned char **size, size_t *len);
+
+/**
+ * @brief Get hash from an Asset pointer.
+ */
+int AssetGetHash(Asset *asset, unsigned char **hash, size_t *len);
+
+/**
+ * @brief Free an Asset pointer.
+ */
+void AssetFree(Asset *src);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct Database. Can be obtained from Schema.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `DatabaseFree` to
+ * release the memory occupied
+ */
+typedef void Database;
+
+/**
+ * @brief Create a Database instance.
+ * @param tables [IN] HashMap, table_name as key
+ * @retval != NULL, a valid pointer of Database.
+ * @attention When passed in, database will take control of the memory management of the vector tables. No more free
+ * is needed. For the database pointer, the memory is managed by Rust. Therefore, to prevent memory leaks,
+ * users should call `DatabaseFree` to release the memory occupied
+ */
+Database *DatabaseNew(
+ unsigned char *name,
+ size_t nameLen,
+ unsigned char *alias,
+ size_t aliasLen,
+ HashMap *tables
+)
+
+/**
+ * @brief Get name from a Database instance.
+ * @param db [IN]
+ * name [OUT]
+ * len [OUT]
+ * @retval SUCCESS
+ * ERRNO_NULLPTR
+ * @attention Name returned shouldn't be freed, or issues of double free is possible.
+ */
+int DatabaseGetName(Database *db, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get alias from a Database instance.
+ * @param db [IN]
+ * alias [OUT]
+ * len [OUT]
+ * @retval SUCCESS
+ * ERRNO_NULLPTR
+ * @attention Alias returned shouldn't be freed, or issues of double free is possible.
+ */
+int DatabaseGetAlias(Database *db, unsigned char **alias, size_t *len);
+
+/**
+ * @brief Get tables from a Database instance.
+ * @param db [IN]
+ * tables [OUT] HashMap, table name as key
+ * @retval SUCCESS
+ * ERRNO_NULLPTR
+ * @attention The HashMap returned should be freed by HashMapFree.
+ */
+int DatabaseGetTable(Database *db, HashMap **tables);
+
+/**
+ * @brief Free a Database pointer.
+ */
+void DatabaseFree(Database *db);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct Table. Can be obtained from Database.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `TableFree` to
+ * release the memory occupied
+ */
+typedef void Table;
+
+/**
+ * @brief Create a Table instance.
+ * fields [IN] Vec
+ * @attention When passed in, table will take control of the memory management of the vector field. No more free is
+ * needed.
+ */
+Table *TableNew(
+ unsigned char *name,
+ size_t nameLen,
+ unsigned char *alias,
+ size_t aliasLen,
+ Vector *fields
+)
+
+/**
+ * @brief Get name from a Table instance.
+ * @param tb [IN]
+ * name [OUT]
+ * len [OUT]
+ * @attention Name returned shouldn't be freed, or issues of double free is possible.
+ */
+int TableGetName(Table *tb, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get alias from a Table instance.
+ * @param tb [IN]
+ * alias [OUT]
+ * len [OUT]
+ * @attention Alias returned shouldn't be freed, or issues of double free is possible.
+ */
+int TableGetAlias(Table *tb, unsigned char **alias, size_t *len);
+
+/**
+ * @brief Get fields from a Database instance.
+ * @param tb [IN]
+ * fields [OUT] Vec
+ * len [OUT]
+ * @attention The Vector returned should be freed by VectorFree.
+ */
+int TableGetFields(Table *tb, Vector **fields);
+
+/**
+ * @brief Free a Table pointer.
+ */
+void TableFree(Table *db);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct Field. Can be obtained from Table.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `FieldFree` to
+ * release the memory occupied
+ */
+typedef void Field;
+
+/**
+ * @brief FieldBuilder providing necessary information when create a Field.
+ */
+typedef struct {
+ unsigned char *colName;
+ size_t colNameLen;
+ unsigned char *alias;
+ size_t aliasLen;
+ unsigned int typ;
+ bool primary;
+ bool nullable;
+} FieldBuilder;
+
+/**
+ * @brief Initialize a Field instance.
+ * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `FieldFree` to
+ * release the memory occupied
+ */
+Field *FieldNew(FieldBuilder *builder);
+
+/**
+ * @brief Get name from a Field instance.
+ * @param fd [IN]
+ * name [OUT]
+ * len [OUT]
+ * @attention Name returned shouldn't be freed, or issues of double free is possible.
+ */
+int FieldGetColName(Field *fd, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get alias from a Field instance.
+ * @param fd [IN]
+ * alias [OUT]
+ * len [OUT]
+ * @attention Alias returned shouldn't be freed, or issues of double free is possible.
+ */
+int FieldGetAlias(Field *fd, unsigned char **alias, size_t *len);
+
+/**
+ * @brief Get type of a Field instance.
+ * @param fd [IN]
+ * typ [OUT]
+ */
+int FieldGetTyp(Field *fd, unsigned int *typ);
+
+/**
+ * @brief Get primacy of a Field instance.
+ * @param fd [IN]
+ * primary [OUT]
+ */
+int FieldGetPrimary(Field *fd, bool *primary);
+
+/**
+ * @brief Get nullability of a Field instance.
+ * @param fd [IN]
+ * nullable [OUT]
+ */
+int FieldGetNullable(Field *fd, bool *nullable);
+
+/**
+ * @brief Free a Field pointer.
+ */
+void FieldFree(Table *db);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct CloudInfo. Can be obtained from
+ * CloudServerGetServiceInfo.
+ * @attention CloudInfo is obtained from CloudServer to provide relevant information to users. When done, users should
+ * call CloudInfoFree to release memory and prevent memory leak.
+ */
+typedef void CloudInfo;
+
+/**
+ * @brief Get user from a CloudInfo pointer.
+ */
+int CloudInfoGetUser(CloudInfo *info, int *user);
+
+/**
+ * @brief Get user from a CloudInfo pointer.
+ */
+int CloudInfoGetId(
+ CloudInfo *info,
+ unsigned char **id,
+ size_t *idLen,
+);
+
+/**
+ * @brief Get total space from a CloudInfo pointer.
+ */
+int CloudInfoGetTotalSpace(
+ CloudInfo *info,
+ int *totalSpace
+);
+
+/**
+ * @brief Get remain space from a CloudInfo pointer.
+ */
+int CloudInfoGetRemainSpace(
+ CloudInfo *info,
+ int *remainSpace
+);
+
+/**
+ * @brief Check whether a CloudInfo enables cloud sync.
+ */
+int CloudInfoGetEnableCloud(
+ CloudInfo *info,
+ bool *enableCloud
+);
+
+/**
+ * @brief Get hashmap of AppInfo from a CloudInfo pointer.
+ * @param appInfo [OUT] HashMap, bundle name as key
+ */
+int CloudInfoGetAppInfo(
+ CloudInfo *info,
+ HashMap **appInfo
+);
+
+/**
+ * @brief Destroy an CloudInfo instance.
+ * @param ptr [IN] The pointer of CloudInfo that should be destroyed.
+ */
+void CloudInfoFree(CloudInfo *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct AppInfo.
+ * @attention AppInfo is obtained from CloudInfo. When CloudInfo is freed, AppInfo will also be
+ * freed.
+ */
+typedef void AppInfo;
+
+/**
+ * @brief Get id from an AppInfo pointer.
+ */
+int AppInfoGetAppId(
+ AppInfo *appInfo,
+ unsigned char **appId,
+ size_t *idLen,
+);
+
+/**
+ * @brief Get bundle name from an AppInfo pointer.
+ */
+int AppInfoGetBundleName(
+ AppInfo *appInfo,
+ unsigned char **bundleName,
+ size_t *len,
+);
+
+/**
+ * @brief Check whether an AppInfo pointer allows cloud switch.
+ */
+int AppInfoGetCloudSwitch(
+ AppInfo *appInfo,
+ bool *cloudSwitch
+);
+
+/**
+ * @brief Get instance id from an AppInfo pointer.
+ */
+int AppInfoGetInstanceId(
+ AppInfo *appInfo,
+ int *id
+);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct SchemaMeta. Can be obtained from
+ * CloudServerGetServiceInfo.
+ * @attention SchemaMeta is obtained from CloudServer to provide relevant information to users. When done, users
+ * should call SchemaMetaFree to release memory and prevent memory leak.
+ */
+typedef void SchemaMeta;
+
+/**
+ * @brief Get version from a SchemaMeta pointer.
+ */
+int SchemaMetaGetVersion(SchemaMeta *schema, int *version);
+
+/**
+ * @brief Get bundle name from a SchemaMeta pointer.
+ */
+int SchemaMetaGetBundleName(SchemaMeta *schema, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get databases from a SchemaMeta instance.
+ * @param schema [IN]
+ * db [IN] Vec
+ * @attention The Vector returned should be freed by VectorFree.
+ */
+int SchemaMetaGetDatabases(
+ SchemaMeta *schema,
+ Vector **db
+);
+
+/**
+ * @brief Destroy an SchemaMeta instance.
+ * @param ptr [IN] The pointer of SchemaMeta that should be destroyed.
+ */
+void SchemaMetaFree(SchemaMeta *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct RelationSet.
+ * @attention When done, users should call `RelationSetFree` to release memory and prevent memory leak.
+ */
+typedef void RelationSet;
+
+/**
+ * @brief Create a RelationSet instance by bundle name, and relations.
+ * @param relations [IN] HashMap, Database alias as key, subscription id and time generated
+ * by the cloud as value
+ * @attention The hashmap passed in will be managed in the Rust side, so don't call free on this pointer again.
+ */
+RelationSet *RelationSetNew(
+ const unsigned char *bundleName,
+ size_t nameLen,
+ c_int expireTime,
+ HashMap *relations
+)
+
+/**
+ * @brief Get bundle name from a RelationSet pointer.
+ */
+int RelationSetGetBundleName(RelationSet *re, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get expire time from a RelationSet pointer.
+ */
+int RelationSetGetExpireTime(RelationSet *re, unsigned int *time);
+
+/**
+ * @brief Get relations from a RelationSet pointer.
+ * @param relations [OUT] HashMap, Database alias as key, subscription id and time generated
+ * by the cloud as value
+ * @attention The hashmap returned should be freed by HashMapFree.
+ */
+int RelationSetGetRelations(RelationSet *re, HashMap **relations);
+
+/**
+ * @brief Free a RelationSet pointer.
+ */
+void RelationSetFree(RelationSet *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct CloudData. Can be obtained from CloudDbBatchQuery.
+ * @attention CloudData is obtained from CloudDatabase to provide relevant information to users. When done, users
+ * should call `CloudDataFree` to release memory and prevent memory leak.
+ */
+typedef void CloudData;
+
+/**
+ * @brief Get the next cursor from a CloudData pointer.
+ */
+int CloudDataGetNextCursor(CloudData *data, unsigned char **cursor, size_t *len);
+
+/**
+ * @brief Check whether a CloudData has more data.
+ */
+int CloudDataGetHasMore(CloudData *data, bool *hasMore);
+
+/**
+ * @brief Get vector of values from a CloudData pointer.
+ * @param values [OUT] Vec
+ * @attention The vector returned should be freed by VectorFree.
+ */
+int CloudDataGetValues(CloudData *data, Vector **values);
+
+/**
+ * @brief Free a CloudData pointer.
+ */
+void CloudDataFree(CloudData *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct ValueBucket.
+ */
+typedef void ValueBucket;
+
+/**
+ * @brief Get id from a ValueBucket pointer.
+ */
+int ValueBucketGetId(ValueBucket *vb, unsigned char **id, size_t *len);
+
+/**
+ * @brief Get type string from a ValueBucket pointer.
+ */
+int ValueBucketGetTyp(ValueBucket *vb, unsigned char **typ, size_t *len);
+
+/**
+ * @brief Get create info from a ValueBucket pointer.
+ */
+int ValueBucketGetCreateInfo(ValueBucket *vb, VBInfo **info);
+
+/**
+ * @brief Get modified info from a ValueBucket pointer.
+ */
+int ValueBucketGetModifiedInfo(ValueBucket *vb, VBInfo **info);
+
+/**
+ * @brief Get data from a ValueBucket pointer.
+ * @param data [OUT] HashMap
+ * @attention The HashMap returned should be freed by `HashMapFree`.
+ */
+int ValueBucketGetData(ValueBucket *vb, HashMap **data);
+
+/**
+ * @brief Check whether a ValueBucket is deleted.
+ */
+int ValueBucketIsDelete(ValueBucket *vb, bool *isDelete);
+
+/**
+ * @brief Get version from a ValueBucket pointer.
+ */
+int ValueBucketGetVersion(ValueBucket *vb, unsigned int *version);
+
+/**
+ * @brief Check whether a ValueBucket is deleted.
+ */
+int ValueBucketIsNewCreate(ValueBucket *vb, bool *isNewCreate);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct VBInfo.
+ */
+typedef void VBInfo;
+
+/**
+ * @brief Get time from a VBInfo pointer.
+ */
+int VBInfoGetTime(VBInfo *info, int *time);
+
+/**
+ * @brief Get device name from a VBInfo pointer.
+ */
+int VBInfoGetDeviceName(VBInfo *info, unsigned char **name, size_t *len);
+
+/**
+ * @brief Get app id from a VBInfo pointer.
+ */
+int VBInfoGetAppId(VBInfo *info, unsigned char **id, size_t *len);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif
+#endif
+
+#endif //CLOUD_EXTENSION_TYPES_H
diff --git a/src/cloud_extension/include/cloud_extension.h b/src/cloud_extension/include/cloud_extension.h
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3f075f0ecab614d09c266638ceeeb35ce057ab65 100644
--- a/src/cloud_extension/include/cloud_extension.h
+++ b/src/cloud_extension/include/cloud_extension.h
@@ -0,0 +1,336 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @defgroup cloud_extension C headers
+ * @ingroup cloud_extension
+ */
+
+#ifndef CLOUD_EXTENSION_H
+#define CLOUD_EXTENSION_H
+
+#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H
+#include "basic_rust_types.h"
+#endif
+
+#ifndef CLOUD_EXTENSION_TYPES_H
+#include "cloud_ext_types.h"
+#endif
+
+#ifndef CLOUD_EXTENSION_ERROR_H
+#include "error.h"
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif
+#endif
+
+// AssetLoader, CloudDatabase, CloudServer
+/**
+ * @brief Type declaration of Rust cloud extension struct AssetLoader.
+ * @attention When an instance is created by AssetLoaderNew,memory is managed by Rust and it will return a pointer.
+ * When destroying this instance, users should call AssetLoaderFree to release memory and prevent leak.
+ *
+ * Besides, AssetLoader is only valid when the database passed in its initialization function is valid.
+ */
+typedef void AssetLoader;
+
+/**
+ * @brief Information needed when upload or download assets through AssetLoader.
+ */
+typedef struct {
+ unsigned char *tableName;
+ size_t tableNameLen;
+ unsigned char *gid;
+ size_t gidLen;
+ unsigned char *prefix;
+ size_t prefixLen;
+} UpDownloadInfo;
+
+/**
+ * @brief Create an AssetLoader instance.
+ * @param userId [IN]
+ * bundleName [IN]
+ * nameLen [IN]
+ * db [IN]
+ */
+AssetLoader *AssetLoaderNew(int userId, const unsigned char *bundleName, size_t nameLen, Database* db);
+
+/**
+ * @brief Upload assets.
+ * @param loader [IN]
+ * tableName [IN]
+ * tableNameLen [IN]
+ * gid [IN]
+ * gidLen [IN]
+ * prefix [IN]
+ * prefixLen [IN]
+ * assets [IN/OUT] HashMap
+ */
+int AssetLoaderUpload(AssetLoader *loader, UpDownloadInfo *info, HashMap *assets);
+
+/**
+ * @brief Download assets.
+ * @param loader [IN]
+ * tableName [IN]
+ * tableNameLen [IN]
+ * gid [IN]
+ * gidLen [IN]
+ * prefix [IN]
+ * prefixLen [IN]
+ * assets [IN/OUT] HashMap
+ */
+int AssetLoaderDownload(AssetLoader *loader, UpDownloadInfo *info, HashMap *assets);
+
+/**
+ * @brief Remove one asset from the local path.
+ * @param asset [IN]
+ */
+int AssetLoaderRemoveLocalAssets(Asset *asset);
+
+/**
+ * @brief Destroy an AssetLoader instance.
+ * @param ptr [IN] The pointer of AssetLoader that should be destroyed.
+ */
+void AssetLoaderFree(AssetLoader *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct CloudDatabase.
+ * @attention When an instance is created by CloudDbNew,memory is managed by Rust and it will return a pointer.
+ * When destroying this instance, users should call CloudDatabaseFree to release memory and prevent leak.
+ */
+typedef void CloudDatabase;
+
+/**
+ * @brief Create an CloudDatabase instance.
+ * @retval != NULL, a valid pointer of CloudDatabase.
+ * @attention Database passed in will be managed by CloudDb instead. No free function is needed to call on it.
+ */
+CloudDatabase *CloudDbNew(
+ int userId,
+ const unsigned char *bundleName,
+ size_t nameLen,
+ Database *database
+);
+
+/**
+ * @brief Sql that will passed to CloudDatabase and executed.
+ */
+typedef struct {
+ unsigned char *table;
+ size_t tableLen;
+ unsigned char *sql;
+ size_t sqlLen;
+} Sql;
+
+/**
+ * @brief Execute a sql on a CloudDatabase instance.
+ * @param cdb [IN]
+ * table [IN]
+ * tableLen [IN]
+ * sql [IN]
+ * sqlLen [IN]
+ * extend [IN/OUT] HashMap
+ * @retval SUCCESS
+ * ERRNO_Unsupported
+ */
+int CloudDbExecuteSql(
+ CloudDatabase *cdb,
+ const Sql *sql,
+ HashMap *extend
+);
+
+/**
+ * @brief Insert batches of value buckets into a CloudDatabase instance.
+ * @param cdb [IN]
+ * table [IN]
+ * tableLen [IN]
+ * value [IN] Vec>
+ * extend [IN/OUT] Vec>
+ * @retval SUCCESS
+ */
+int CloudDbBatchInsert(
+ CloudDatabase *cdb,
+ const unsigned char *table,
+ size_t tableLen,
+ const Vector *value,
+ Vector *extend,
+);
+
+/**
+ * @brief Update batches of value buckets in a CloudDatabase instance.
+ * @param cdb [IN]
+ * table [IN]
+ * tableLen [IN]
+ * value [IN] Vec>
+ * extend [IN/OUT] Vec>
+ * @retval SUCCESS
+ */
+int CloudDbBatchUpdate(
+ CloudDatabase *cdb,
+ const unsigned char *table,
+ size_t tableLen,
+ const Vector *value,
+ Vector *extend
+);
+
+/**
+ * @brief Delete batches of value buckets from a CloudDatabase instance.
+ * @param cdb [IN]
+ * table [IN]
+ * tableLen [IN]
+ * extend [IN/OUT] Vec>
+ * @retval SUCCESS
+ */
+int CloudDbBatchDelete(
+ CloudDatabase *cdb,
+ const unsigned char *table,
+ size_t tableLen,
+ Vector *extend
+);
+
+/**
+ * @brief Query info that will passed to CloudDatabase.
+ */
+typedef struct {
+ unsigned char *table;
+ size_t tableLen;
+ unsigned char *cursor;
+ size_t cursorLen;
+} QueryInfo;
+
+
+/**
+ * @brief Search batches of value buckets from a CloudDatabase instance.
+ * @param cdb [IN]
+ * table [IN]
+ * tableLen [IN]
+ * cursor [IN]
+ * cursorLen [IN]
+ * out [OUT] The address of a pointer to a Cursor. The pointer value will be updated when
+ * query function successfully returns.
+ * @attention The CloudData returned should be freed by `CloudDataFree` if no longer in use.
+ */
+int CloudDbBatchQuery(
+ CloudDatabase *cdb,
+ const QueryInfo *info,
+ CloudData **out
+);
+
+/**
+ * @brief Lock a CloudDatabase instance.
+ * @param cdb [IN]
+ * expire [OUT]
+ */
+int CloudDbLock(CloudDatabase *cdb, int *expire);
+
+/**
+ * @brief Unlock a CloudDatabase instance.
+ * @param cdb [IN]
+ */
+int CloudDbUnlock(CloudDatabase *cdb);
+
+/**
+ * @brief Heartbeat function of a CloudDatabase. Extend the current locking session.
+ * @param cdb [IN]
+ */
+int CloudDbHeartbeat(CloudDatabase *cdb);
+
+/**
+ * @brief Destroy an CloudDatabase instance.
+ * @param ptr [IN] The pointer of CloudDatabase that should be destroyed.
+ */
+void CloudDatabaseFree(CloudDatabase *ptr);
+
+/**
+ * @brief Type declaration of Rust cloud extension struct CloudServer.
+ * @attention When an instance is created by CloudServerNew,memory is managed by Rust and it will return a pointer.
+ * When destroying this instance, users should call CloudServerFree to release memory and prevent leak.
+ */
+typedef void CloudServer;
+
+/**
+ * @brief Create an CloudServer instance.
+ * @param userId [IN]
+ * @retval != NULL, a valid pointer of CloudServer.
+ */
+CloudServer *CloudServerNew(int userId);
+
+/**
+ * @brief Get service info from a CloudServer pointer.
+ * @param server [IN]
+ * info [OUT]
+ * @attention The CloudInfo returned should be freed by `CloudInfoFree` if no longer in use.
+ */
+int CloudServerGetServiceInfo(CloudServer *server, CloudInfo **info);
+
+/**
+ * @brief Get app schema from a CloudServer pointer, with a bundle name.
+ * @param server [IN]
+ * bundleName [IN]
+ * bundleNameLen [IN]
+ * schema [OUT]
+ * @attention The SchemaMeta returned should be freed by `SchemaMetaFree` if no longer in use.
+ */
+int CloudServerGetAppSchema(
+ CloudServer *server,
+ const unsigned char *bundleName,
+ size_t bundleNameLen,
+ SchemaMeta **schema
+);
+
+/**
+ * @brief Pass a batch of subscription orders to a CloudServer pointer, with target database information
+ * and expected expire time.
+ * @param server [IN]
+ * dbs [IN] HashMap>, bundle name as key
+ * relations [OUT] HashMap, bundle name as key
+ * err [OUT] Vec
+ * @attention The Vector returned should be freed by `VectorFree`. Similar for the HashMap.
+ */
+int CloudServerSubscribe(
+ CloudServer *server,
+ const HashMap *dbs,
+ int expire,
+ HashMap **relations,
+ Vector **err,
+);
+
+/**
+ * @brief Pass a batch of unsubscription orders to a CloudServer pointer, with target relations.
+ * @param server [IN]
+ * relations [IN] HashMap>, bundle name as key, ids as value
+ * err [OUT] Vec
+ * @attention The Vector returned should be freed by `VectorFree`.
+ */
+int CloudServerUnsubscribe(
+ CloudServer *server,
+ const HashMap *relations,
+ Vector **err,
+);
+
+/**
+ * @brief Destroy an CloudServer instance.
+ * @param ptr [IN] The pointer of CloudServer that should be destroyed.
+ */
+void CloudServerFree(CloudServer *ptr);
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif
+#endif
+
+#endif // CLOUD_EXTENSION_H
\ No newline at end of file
diff --git a/src/cloud_extension/include/error.h b/src/cloud_extension/include/error.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2ff0d8d175777007dfab9802c023cb22fbbcd41
--- /dev/null
+++ b/src/cloud_extension/include/error.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CLOUD_EXTENSION_ERROR_H
+#define CLOUD_EXTENSION_ERROR_H
+
+
+#ifdef __cplusplus
+#if __cplusplus
+extern "C" {
+#endif
+#endif
+
+#define SUCCESS 0
+#define ERRNO_NULLPTR 1
+#define ERRNO_UNKNOWN 2
+#define ERRNO_INVALID_INPUT_TYPE 3
+#define ERRNO_ASSET_DOWNLOAD_FAILURE 4
+#define ERRNO_ASSET_UPLOAD_FAILURE 5
+#define ERRNO_UNSUPPORTED 6
+#define ERRNO_NETWORK_ERROR 7
+#define ERRNO_LOCKED_BY_OTHERS 8
+#define ERRNO_UNLOCKED 9
+#define ERRNO_RECORD_LIMIT_EXCEEDED 10
+#define ERRNO_NO_SPACE_FOR_ASSET 11
+#define ERRNO_OTHER_IPC_ERROR 12
+#define ERRNO_IPC_ERRORS 13
+#define ERRNO_OTHER_IO_ERROR 14
+#define ERRNO_OUT_OF_RANGE 15
+#define ERRNO_NO_SUCH_TABLE_IN_DB 16
+
+
+#ifdef __cplusplus
+#if __cplusplus
+}
+#endif
+#endif
+
+#endif //CLOUD_EXTENSION_ERROR_H
diff --git a/src/cloud_extension/src/c_adapter/basic_rust_types.rs b/src/cloud_extension/src/c_adapter/basic_rust_types.rs
new file mode 100644
index 0000000000000000000000000000000000000000..484d31b3d4e6573ff1df66e6b0fc3f17612f5e78
--- /dev/null
+++ b/src/cloud_extension/src/c_adapter/basic_rust_types.rs
@@ -0,0 +1,751 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::c_adapter::*;
+use crate::service_impl::{asset_loader, cloud_db, cloud_service, types};
+use std::collections;
+use std::ffi::{c_int, c_uchar, c_void};
+use std::ptr::{null, null_mut};
+
+pub type Vector = c_void;
+pub type HashMap = c_void;
+
+/// Value type enum in C. Used to represent returning type value, so C side can cast its pointer.
+#[repr(C)]
+#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
+pub enum ValueType {
+ NULL = 0,
+ U32,
+ I32,
+ STRING,
+ VALUE,
+ VALUE_BUCKET,
+ DATABASE,
+ TABLE,
+ FIELD,
+ RELATION,
+ RELATION_SET,
+ ASSET,
+ APP_INFO,
+ VEC_U32,
+ VEC_DATABASE,
+ HASHMAP_VALUE,
+}
+
+/// Vector enum in C side to adapt Vector generic types.
+pub enum VectorCffi {
+ I32(Vec),
+ U32(Vec),
+ String(Vec),
+ Value(Vec),
+ ValueBucket(Vec),
+ Database(Vec),
+ Table(Vec),
+ Field(Vec),
+ RelationSet(Vec),
+ Asset(Vec),
+ AppInfo(Vec),
+ VecU32(Vec>),
+ VecDatabase(Vec>),
+ HashMapValue(Vec>),
+}
+
+/// Hashmap enum in C side to adapt Vector generic types. String as the key, enum type only
+/// marks value type.
+pub enum HashMapCffi {
+ U32(collections::HashMap),
+ Value(collections::HashMap),
+ Table(collections::HashMap),
+ RelationSet(collections::HashMap),
+ AppInfo(collections::HashMap),
+ VecU32(collections::HashMap>),
+ VecDatabase(collections::HashMap>),
+}
+
+/// Create a Vector enum according to ValueType.
+#[no_mangle]
+pub unsafe extern "C" fn VectorNew(typ: ValueType) -> *mut Vector {
+ let vec = match typ {
+ ValueType::I32 => VectorCffi::I32(vec![]),
+ ValueType::U32 => VectorCffi::U32(vec![]),
+ ValueType::STRING => VectorCffi::String(vec![]),
+ ValueType::VALUE => VectorCffi::Value(vec![]),
+ ValueType::VALUE_BUCKET => VectorCffi::ValueBucket(vec![]),
+ ValueType::DATABASE => VectorCffi::Database(vec![]),
+ ValueType::TABLE => VectorCffi::Table(vec![]),
+ ValueType::FIELD => VectorCffi::Field(vec![]),
+ ValueType::RELATION_SET => VectorCffi::RelationSet(vec![]),
+ ValueType::ASSET => VectorCffi::Asset(vec![]),
+ ValueType::APP_INFO => VectorCffi::AppInfo(vec![]),
+ ValueType::VEC_U32 => VectorCffi::VecU32(vec![]),
+ ValueType::VEC_DATABASE => VectorCffi::VecDatabase(vec![]),
+ ValueType::HASHMAP_VALUE => VectorCffi::HashMapValue(vec![]),
+ // Not supported
+ _ => return null_mut(),
+ };
+ Box::into_raw(Box::from(vec)) as *mut Vector
+}
+
+/// Get ValueType of the Vector pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VectorGetValueTyp(vector: *const Vector) -> ValueType {
+ if vector.is_null() {
+ return ValueType::NULL;
+ }
+ let vector = &*(vector as *mut VectorCffi);
+ match vector {
+ VectorCffi::I32(_) => ValueType::I32,
+ VectorCffi::U32(_) => ValueType::U32,
+ VectorCffi::String(_) => ValueType::STRING,
+ VectorCffi::Value(_) => ValueType::VALUE,
+ VectorCffi::ValueBucket(_) => ValueType::VALUE_BUCKET,
+ VectorCffi::Database(_) => ValueType::DATABASE,
+ VectorCffi::Table(_) => ValueType::TABLE,
+ VectorCffi::Field(_) => ValueType::FIELD,
+ VectorCffi::RelationSet(_) => ValueType::RELATION_SET,
+ VectorCffi::Asset(_) => ValueType::ASSET,
+ VectorCffi::AppInfo(_) => ValueType::APP_INFO,
+ VectorCffi::VecU32(_) => ValueType::VEC_U32,
+ VectorCffi::VecDatabase(_) => ValueType::VEC_DATABASE,
+ VectorCffi::HashMapValue(_) => ValueType::HASHMAP_VALUE,
+ }
+}
+
+/// Push value into the Vector pointer. If the value pushed is allocated in the Rust side before,
+/// pushing them means to transfer their management to the Vector, so no more free is needed.
+#[no_mangle]
+pub unsafe extern "C" fn VectorPush(
+ vector: *mut Vector,
+ value: *mut c_void,
+ value_len: usize,
+) -> c_int {
+ if vector.is_null() | value.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vector = &mut *(vector as *mut VectorCffi);
+ match vector {
+ VectorCffi::I32(vec) => {
+ let ptr = value as *mut i32;
+ vec.push(*ptr);
+ }
+ VectorCffi::U32(vec) => {
+ let ptr = value as *mut u32;
+ vec.push(*ptr);
+ }
+ VectorCffi::String(vec) => {
+ let str = char_ptr_to_string(value as *const c_uchar, value_len);
+ vec.push(str);
+ }
+ VectorCffi::Value(vec) => {
+ let val = *Box::from_raw(value as *mut types::Value);
+ vec.push(val);
+ }
+ VectorCffi::ValueBucket(vec) => {
+ let vb = *Box::from_raw(value as *mut cloud_db::ValueBucket);
+ vec.push(vb);
+ }
+ VectorCffi::Database(vec) => {
+ let db = *Box::from_raw(value as *mut types::Database);
+ vec.push(db);
+ }
+ VectorCffi::Table(vec) => {
+ let val = *Box::from_raw(value as *mut types::Table);
+ vec.push(val);
+ }
+ VectorCffi::Field(vec) => {
+ let fd = *Box::from_raw(value as *mut types::Field);
+ vec.push(fd);
+ }
+ VectorCffi::RelationSet(vec) => {
+ let val = *Box::from_raw(value as *mut cloud_service::RelationSet);
+ vec.push(val);
+ }
+ VectorCffi::Asset(vec) => {
+ let asset = *Box::from_raw(value as *mut asset_loader::Asset);
+ vec.push(asset);
+ }
+ VectorCffi::AppInfo(vec) => {
+ let val = *Box::from_raw(value as *mut cloud_service::AppInfo);
+ vec.push(val);
+ }
+ VectorCffi::VecU32(vec) => {
+ let vec_struct = *Box::from_raw(value as *mut VectorCffi);
+ match vec_struct {
+ VectorCffi::U32(v) => vec.push(v),
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ }
+ }
+ VectorCffi::VecDatabase(vec) => {
+ let vec_struct = *Box::from_raw(value as *mut VectorCffi);
+ match vec_struct {
+ VectorCffi::Database(v) => vec.push(v),
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ }
+ }
+ VectorCffi::HashMapValue(vec) => {
+ let map = *Box::from_raw(value as *mut HashMapCffi);
+ match map {
+ HashMapCffi::Value(m) => vec.push(m),
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ }
+ }
+ }
+ SUCCESS
+}
+
+/// Get value from the Vector pointer. If returning type is `Vector` or `HashMap` pointer,
+/// these pointers should be freed by `VectorFree` or `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn VectorGet(
+ vector: *mut Vector,
+ index: usize,
+ value: *mut *const c_void,
+ _value_len: *mut usize,
+) -> c_int {
+ macro_rules! get_content {
+ ($vec: ident, $index: ident, $value: ident) => {
+ if $index < $vec.len() {
+ *$value = (&$vec[index]) as *const _ as *const c_void;
+ } else {
+ return ERRNO_OUT_OF_RANGE;
+ }
+ };
+ }
+
+ if vector.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let vector = &mut *(vector as *mut VectorCffi);
+ match vector {
+ VectorCffi::I32(vec) => get_content!(vec, index, value),
+ VectorCffi::U32(vec) => get_content!(vec, index, value),
+ VectorCffi::String(vec) => get_content!(vec, index, value),
+ VectorCffi::Value(vec) => get_content!(vec, index, value),
+ VectorCffi::ValueBucket(vec) => get_content!(vec, index, value),
+ VectorCffi::Database(vec) => get_content!(vec, index, value),
+ VectorCffi::Table(vec) => get_content!(vec, index, value),
+ VectorCffi::Field(vec) => get_content!(vec, index, value),
+ VectorCffi::RelationSet(vec) => get_content!(vec, index, value),
+ VectorCffi::Asset(vec) => get_content!(vec, index, value),
+ VectorCffi::AppInfo(vec) => get_content!(vec, index, value),
+ VectorCffi::VecU32(vec) => {
+ if index < vec.len() {
+ let src = &vec[index];
+ *value = Box::into_raw(Box::new(VectorCffi::U32(src.clone()))) as *const c_void;
+ } else {
+ return ERRNO_OUT_OF_RANGE;
+ }
+ }
+ VectorCffi::VecDatabase(vec) => {
+ if index < vec.len() {
+ let src = &vec[index];
+ *value =
+ Box::into_raw(Box::new(VectorCffi::Database(src.clone()))) as *const c_void;
+ } else {
+ return ERRNO_OUT_OF_RANGE;
+ }
+ }
+ VectorCffi::HashMapValue(vec) => {
+ if index < vec.len() {
+ let src = &vec[index];
+ let mut inner = collections::HashMap::new();
+ for (key, value) in src {
+ inner.insert(key.clone(), value.clone());
+ }
+ *value = Box::into_raw(Box::new(HashMapCffi::Value(inner))) as *const c_void;
+ } else {
+ return ERRNO_OUT_OF_RANGE;
+ }
+ }
+ }
+ SUCCESS
+}
+
+/// Get length of a Vector pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VectorGetLength(vector: *mut Vector, len: *mut usize) -> c_int {
+ if vector.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let vector = &mut *(vector as *mut VectorCffi);
+ match vector {
+ VectorCffi::I32(vec) => *len = vec.len(),
+ VectorCffi::U32(vec) => *len = vec.len(),
+ VectorCffi::String(vec) => *len = vec.len(),
+ VectorCffi::Value(vec) => *len = vec.len(),
+ VectorCffi::ValueBucket(vec) => *len = vec.len(),
+ VectorCffi::Database(vec) => *len = vec.len(),
+ VectorCffi::Table(vec) => *len = vec.len(),
+ VectorCffi::Field(vec) => *len = vec.len(),
+ VectorCffi::RelationSet(vec) => *len = vec.len(),
+ VectorCffi::Asset(vec) => *len = vec.len(),
+ VectorCffi::AppInfo(vec) => *len = vec.len(),
+ VectorCffi::VecU32(vec) => *len = vec.len(),
+ VectorCffi::VecDatabase(vec) => *len = vec.len(),
+ VectorCffi::HashMapValue(vec) => *len = vec.len(),
+ }
+ SUCCESS
+}
+
+/// Free a Vector pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VectorFree(vector: *mut Vector) {
+ if !vector.is_null() {
+ let _ = Box::from_raw(vector as *mut VectorCffi);
+ }
+}
+
+/// Initialize a HashMap enum by its ValueType. The key type is fixed to be String.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapNew(value_type: ValueType) -> *mut HashMap {
+ let map = match value_type {
+ ValueType::U32 => HashMapCffi::U32(collections::HashMap::default()),
+ ValueType::VALUE => HashMapCffi::Value(collections::HashMap::default()),
+ ValueType::TABLE => HashMapCffi::Table(collections::HashMap::default()),
+ ValueType::RELATION => HashMapCffi::RelationSet(collections::HashMap::default()),
+ ValueType::APP_INFO => HashMapCffi::AppInfo(collections::HashMap::default()),
+ ValueType::VEC_U32 => HashMapCffi::VecU32(collections::HashMap::default()),
+ ValueType::VEC_DATABASE => HashMapCffi::VecDatabase(collections::HashMap::default()),
+ // Not supported
+ _ => return null_mut(),
+ };
+ Box::into_raw(Box::from(map)) as *mut HashMap
+}
+
+/// Get key type of a Hashmap pointer.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapGetKeyTyp(hash_map: *mut HashMap) -> ValueType {
+ if hash_map.is_null() {
+ ValueType::NULL
+ } else {
+ ValueType::STRING
+ }
+}
+
+/// Get value type of a Hashmap pointer.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapGetValueTyp(hash_map: *mut HashMap) -> ValueType {
+ if hash_map.is_null() {
+ return ValueType::NULL;
+ }
+ let hash_map = &*(hash_map as *mut HashMapCffi);
+ match hash_map {
+ HashMapCffi::U32(_) => ValueType::U32,
+ HashMapCffi::Value(_) => ValueType::VALUE,
+ HashMapCffi::Table(_) => ValueType::TABLE,
+ HashMapCffi::RelationSet(_) => ValueType::RELATION,
+ HashMapCffi::AppInfo(_) => ValueType::APP_INFO,
+ HashMapCffi::VecU32(_) => ValueType::VEC_U32,
+ HashMapCffi::VecDatabase(_) => ValueType::VEC_DATABASE,
+ }
+}
+
+/// Get length of a Hashmap pointer.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapGetLength(hash_map: *mut HashMap, len: *mut usize) -> c_int {
+ if hash_map.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let hash_map = &mut *(hash_map as *mut HashMapCffi);
+ match hash_map {
+ HashMapCffi::U32(map) => *len = map.len(),
+ HashMapCffi::Value(map) => *len = map.len(),
+ HashMapCffi::Table(map) => *len = map.len(),
+ HashMapCffi::RelationSet(map) => *len = map.len(),
+ HashMapCffi::AppInfo(map) => *len = map.len(),
+ HashMapCffi::VecU32(map) => *len = map.len(),
+ HashMapCffi::VecDatabase(map) => *len = map.len(),
+ }
+ SUCCESS
+}
+
+/// Insert key, value pairs into the Hashmap pointer. If the value pushed is allocated in the Rust
+/// side before, pushing them means to transfer their management to the Hashmap, so no more free
+/// is needed.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapInsert(
+ hash_map: *mut HashMap,
+ key: *mut c_uchar,
+ key_len: usize,
+ value: *mut c_void,
+ _value_len: usize,
+) -> c_int {
+ if hash_map.is_null() | key.is_null() | value.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let hash_map = &mut *(hash_map as *mut HashMapCffi);
+ let key = char_ptr_to_string(key as *const c_uchar, key_len);
+ match hash_map {
+ HashMapCffi::U32(map) => {
+ let ptr = value as *mut u32;
+ map.insert(key, *ptr);
+ }
+ HashMapCffi::Value(map) => {
+ let val = *Box::from_raw(value as *mut types::Value);
+ map.insert(key, val);
+ }
+ HashMapCffi::Table(map) => {
+ let val = *Box::from_raw(value as *mut types::Table);
+ map.insert(key, val);
+ }
+ HashMapCffi::RelationSet(map) => {
+ let val = *Box::from_raw(value as *mut cloud_service::RelationSet);
+ map.insert(key, val);
+ }
+ HashMapCffi::AppInfo(map) => {
+ let val = *Box::from_raw(value as *mut cloud_service::AppInfo);
+ map.insert(key, val);
+ }
+ HashMapCffi::VecU32(map) => {
+ let val = *Box::from_raw(value as *mut VectorCffi);
+ match val {
+ VectorCffi::U32(vec) => map.insert(key, vec),
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+ }
+ HashMapCffi::VecDatabase(map) => {
+ let val = *Box::from_raw(value as *mut VectorCffi);
+ match val {
+ VectorCffi::Database(vec) => map.insert(key, vec),
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+ }
+ }
+ SUCCESS
+}
+
+/// Get key, value pair from the Hashmap pointer. The returning type is `Vector`, and should be
+/// freed by `VectorFree`.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapIterGetKeyValuePair(
+ hash_map: *mut HashMap,
+ key: *mut *const Vector,
+ value: *mut *const Vector,
+) -> c_int {
+ if hash_map.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let hash_map = &mut *(hash_map as *mut HashMapCffi);
+ match hash_map {
+ HashMapCffi::U32(map) => {
+ let key_vec = map.keys().cloned().collect();
+ let value_vec: Vec = map.values().cloned().collect();
+
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::U32(value_vec))) as *const Vector;
+ }
+ HashMapCffi::Value(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let value_vec: Vec = map.values().cloned().collect();
+
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::Value(value_vec))) as *const Vector;
+ }
+ HashMapCffi::Table(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let mut value_vec = vec![];
+ for src in map.values() {
+ value_vec.push(src.clone());
+ }
+
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::Table(value_vec))) as *const Vector;
+ }
+ HashMapCffi::RelationSet(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let mut value_vec = vec![];
+ for src in map.values() {
+ value_vec.push(src.clone());
+ }
+
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::RelationSet(value_vec))) as *const Vector;
+ }
+ HashMapCffi::AppInfo(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let mut value_vec = vec![];
+ for src in map.values() {
+ value_vec.push(src.clone());
+ }
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::AppInfo(value_vec))) as *const Vector;
+ }
+ HashMapCffi::VecU32(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let value_vec: Vec> = map.values().cloned().collect();
+
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::VecU32(value_vec))) as *const Vector;
+ }
+ HashMapCffi::VecDatabase(map) => {
+ let key_vec: Vec = map.keys().cloned().collect();
+ let mut value_vec = vec![];
+ for src in map.values() {
+ value_vec.push(src.clone());
+ }
+ *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector;
+ *value = Box::into_raw(Box::new(VectorCffi::VecDatabase(value_vec))) as *const Vector;
+ }
+ }
+ SUCCESS
+}
+
+/// According to key, get value from the Hashmap pointer. If returning type is `Vector` or `HashMap`
+/// pointer, these pointers should be freed by `VectorFree` or `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapGet(
+ hash_map: *mut HashMap,
+ key: *mut c_uchar,
+ key_len: usize,
+ value: *mut *const c_void,
+ value_len: *mut usize,
+) -> c_int {
+ if hash_map.is_null() | key.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let hash_map = &mut *(hash_map as *mut HashMapCffi);
+ let key = String::from_raw_parts(key as *mut u8, key_len, key_len);
+ *value = null();
+ *value_len = 0;
+
+ match hash_map {
+ HashMapCffi::U32(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = val as *const _ as *const c_void;
+ }
+ }
+ HashMapCffi::Value(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = val as *const _ as *const c_void;
+ }
+ }
+ HashMapCffi::Table(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = val as *const _ as *const c_void;
+ }
+ }
+ HashMapCffi::RelationSet(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = val as *const _ as *const c_void;
+ }
+ }
+ HashMapCffi::AppInfo(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = val as *const _ as *const c_void;
+ }
+ }
+ HashMapCffi::VecU32(map) => {
+ if let Some(val) = map.get(&key) {
+ *value = Box::into_raw(Box::new(VectorCffi::U32(val.clone()))) as *const c_void;
+ }
+ }
+ HashMapCffi::VecDatabase(map) => {
+ if let Some(val) = map.get(&key) {
+ *value =
+ Box::into_raw(Box::new(VectorCffi::Database(val.clone()))) as *const c_void;
+ }
+ }
+ }
+ SUCCESS
+}
+
+/// Free a Hashmap pointer.
+#[no_mangle]
+pub unsafe extern "C" fn HashMapFree(hash_map: *mut HashMap) {
+ if !hash_map.is_null() {
+ let _ = Box::from_raw(hash_map as *mut HashMapCffi);
+ }
+}
+
+#[cfg(tests)]
+mod test {
+ use crate::c_adapter::basic_rust_types::*;
+ use crate::c_adapter::*;
+ use crate::service_impl::{asset_loader, cloud_db, cloud_service, types};
+
+ /// UT test for vec use and create.
+ ///
+ /// # Brief
+ /// 1. Create a vec of different type.
+ /// 2. Push and get values from it.
+ /// 3. Free vec.
+ /// 4. No error and memory leak should happen.
+ macro_rules! ut_vec {
+ (
+ $typ: ident,
+ $push: ident,
+ $value_typ: ty,
+ $value: ident
+ ) => {
+ unsafe {
+ let typ = ValueType::$typ;
+ let new_vec = VectorNew(typ);
+ assert!(!new_vec.is_null());
+ match VectorGetValueTyp(new_vec) {
+ ValueType::$typ => {}
+ _ => panic!("Vector Type wrong"),
+ }
+
+ let mut length;
+ assert_eq!(
+ VectorGetLength(new_vec, &mut length as *mut _ as *mut usize),
+ SUCCESS
+ );
+ assert_eq!(length, 0);
+
+ assert_eq!(
+ VectorPush(new_vec, $push as *const _ as *mut c_void, 0),
+ SUCCESS
+ );
+ assert_eq!(
+ VectorGetLength(new_vec, &mut length as *mut _ as *mut usize),
+ SUCCESS
+ );
+ assert_eq!(length, 1);
+
+ let mut val = null();
+ assert_eq!(
+ VectorGet(
+ new_vec,
+ 0,
+ &mut val as *mut _ as *mut *const c_void,
+ &mut length as *mut _ as *mut usize
+ ),
+ SUCCESS
+ );
+ let value = &*(val as *const $value_typ);
+ assert_eq!(value, $value);
+ VectorFree(new_vec);
+ }
+ };
+ }
+
+ /// UT test for hashmap use and create.
+ ///
+ /// # Brief
+ /// 1. Create a hashmap of different type.
+ /// 2. Push and get values from it.
+ /// 3. Free vec.
+ /// 4. No error and memory leak should happen.
+ macro_rules! ut_vec {
+ (
+ $typ: ident,
+ $push: ident,
+ $value_typ: ty,
+ $value: ident
+ ) => {
+ unsafe {
+ let typ = ValueType::$typ;
+ let new_vec = VectorNew(typ);
+ assert!(!new_vec.is_null());
+ match VectorGetValueTyp(new_vec) {
+ ValueType::$typ => {}
+ _ => panic!("Vector Type wrong"),
+ }
+
+ let mut length;
+ assert_eq!(
+ VectorGetLength(new_vec, &mut length as *mut _ as *mut usize),
+ SUCCESS
+ );
+ assert_eq!(length, 0);
+
+ assert_eq!(
+ VectorPush(new_vec, $push as *const _ as *mut c_void, 0),
+ SUCCESS
+ );
+ assert_eq!(
+ VectorGetLength(new_vec, &mut length as *mut _ as *mut usize),
+ SUCCESS
+ );
+ assert_eq!(length, 1);
+
+ let mut val = null();
+ assert_eq!(
+ VectorGet(
+ new_vec,
+ 0,
+ &mut val as *mut _ as *mut *const c_void,
+ &mut length as *mut _ as *mut usize
+ ),
+ SUCCESS
+ );
+ let value = &*(val as *const $value_typ);
+ assert_eq!(value, $value);
+ VectorFree(new_vec);
+ }
+ };
+ }
+
+ #[test]
+ fn ut_vec_i32() {
+ let src: i32 = 3;
+ let borrow = &src;
+ ut_vec!(I32, borrow, i32, src);
+ }
+
+ #[test]
+ fn ut_vec_u32() {
+ let src: u32 = 3;
+ let borrow = &src;
+ ut_vec!(U32, borrow, u32, src);
+ }
+
+ #[test]
+ fn ut_vec_string() {
+ let src = "hello".to_string();
+ let borrow = &src;
+ ut_vec!(String, borrow, String, src);
+ }
+
+ #[test]
+ fn ut_vec_value() {
+ let src = types::Value::Empty;
+ let borrow = &src;
+ ut_vec!(Value, borrow, types::Value, src);
+ }
+
+ #[test]
+ fn ut_vec_vecu32() {
+ unsafe {
+ let vec = VectorNew(ValueType::U32);
+ let src: u32 = 3;
+ assert_eq!(VectorPush(vec, &src as *mut _ as *mut c_void, 1), SUCCESS);
+ }
+
+ let src = vec![1_u32];
+ ut_vec!(VecU32, vec, Vec, src);
+ }
+
+ #[test]
+ fn ut_vec_null() {
+ unsafe {
+ assert!(VectorNew(ValueType::NULL).is_null());
+ assert_eq!(
+ VectorPush(null_mut(), &3 as *mut _ as *mut c_void, 0),
+ ERRNO_NULLPTR
+ );
+
+ let typ = ValueType::I32;
+ let new_vec = VectorNew(typ);
+ let mut length;
+ assert_eq!(
+ VectorGetLength(null_mut(), &mut length as *mut _ as *mut usize),
+ ERRNO_NULLPTR
+ );
+ assert_eq!(VectorPush(new_vec, null_mut(), 0), ERRNO_NULLPTR);
+ VectorFree(new_vec);
+ }
+ }
+}
diff --git a/src/cloud_extension/src/c_adapter/cloud_ext_types.rs b/src/cloud_extension/src/c_adapter/cloud_ext_types.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3ad7eda96772e1e275d33703e64d171cff2a1b52
--- /dev/null
+++ b/src/cloud_extension/src/c_adapter/cloud_ext_types.rs
@@ -0,0 +1,1166 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::c_adapter::basic_rust_types;
+use crate::c_adapter::basic_rust_types::{HashMapCffi, VectorCffi};
+use crate::c_adapter::*;
+use crate::service_impl::{asset_loader, cloud_db, cloud_service, types};
+use std::ffi::{c_double, c_int, c_uchar, c_uint, c_void};
+use std::ptr::{null, null_mut, slice_from_raw_parts};
+
+/// AssetBuilder defined in C struct. It will be used to build a Rust Asset struct in use.
+#[repr(C)]
+pub struct AssetBuilder {
+ pub version: u64,
+ pub status: asset_loader::AssetStatus,
+ pub expires_time: u64,
+ pub id: *const c_uchar,
+ pub id_len: usize,
+ pub name: *const c_uchar,
+ pub name_len: usize,
+ pub uri: *const c_uchar,
+ pub uri_len: usize,
+ pub local_path: *const c_uchar,
+ pub local_path_len: usize,
+ pub create_time: *const c_uchar,
+ pub create_time_len: usize,
+ pub modify_time: *const c_uchar,
+ pub modify_time_len: usize,
+ pub size: *const c_uchar,
+ pub size_len: usize,
+ pub hash: *const c_uchar,
+ pub hash_len: usize,
+}
+
+/// Enumeration represents inner type of contents in Value enum.
+#[repr(C)]
+#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
+pub enum ValueInnerType {
+ EMPTY = 0,
+ INT,
+ DOUBLE,
+ BOOL,
+ STRING,
+ BYTES,
+ ASSET,
+ ASSETS,
+}
+
+/// Value pointer passed to C side.
+pub type Value = c_void;
+/// Asset pointer passed to C side.
+pub type Asset = c_void;
+/// Database pointer passed to C side.
+pub type Database = c_void;
+/// CloudInfo pointer passed to C side.
+pub type CloudInfo = c_void;
+/// AppInfo pointer passed to C side.
+pub type AppInfo = c_void;
+/// Table pointer passed to C side.
+pub type Table = c_void;
+/// Field pointer passed to C side.
+pub type Field = c_void;
+/// SchemaMeta pointer passed to C side.
+pub type SchemaMata = c_void;
+/// RelationSet pointer passed to C side.
+pub type RelationSet = c_void;
+/// CloudData pointer passed to C side.
+pub type CloudData = c_void;
+/// ValueBucket pointer passed to C side.
+pub type ValueBucket = c_void;
+/// VBInfo pointer passed to C side.
+pub type VBInfo = c_void;
+
+/// Create a Value instance according to ValueInnerType.
+#[no_mangle]
+pub unsafe extern "C" fn ValueNew(
+ typ: ValueInnerType,
+ content: *mut c_void,
+ len: usize,
+) -> *mut Value {
+ if content.is_null() {
+ return null_mut();
+ }
+
+ let val = match typ {
+ ValueInnerType::EMPTY => types::Value::Empty,
+ ValueInnerType::INT => {
+ let int = &*(content as *mut c_int as *mut i64);
+ types::Value::Int(*int)
+ }
+ ValueInnerType::DOUBLE => {
+ let float = &*(content as *mut c_int as *mut f64);
+ types::Value::Double(*float)
+ }
+ ValueInnerType::BOOL => {
+ let bool = &*(content as *mut c_int);
+ types::Value::Bool(c_int_to_bool(*bool))
+ }
+ ValueInnerType::STRING => {
+ let str = char_ptr_to_string(content as *const c_uchar, len);
+ types::Value::String(str)
+ }
+ ValueInnerType::BYTES => {
+ let slice = &(*slice_from_raw_parts(content as *const u8, len));
+ types::Value::Bytes(slice.to_vec())
+ }
+ ValueInnerType::ASSET => {
+ let src = &*(content as *mut AssetBuilder);
+ let asset = get_asset(src);
+ types::Value::Asset(asset)
+ }
+ ValueInnerType::ASSETS => {
+ let asset_slice = &*slice_from_raw_parts(content as *const AssetBuilder, len);
+ let mut assets = vec![];
+ for asset_cffi in asset_slice {
+ assets.push(get_asset(asset_cffi));
+ }
+ types::Value::Assets(assets)
+ }
+ };
+
+ Box::into_raw(Box::new(val)) as *mut Value
+}
+
+/// Get content from a Value pointer.
+///
+/// If the Value is Value::Assets, the Vector pointer returned should be freed by
+/// `VectorFree`.
+#[no_mangle]
+pub unsafe extern "C" fn ValueGetContent(
+ src: *mut Value,
+ typ: *mut ValueInnerType,
+ content: *mut *const c_void,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let value_struct = &*(src as *const types::Value);
+ match value_struct {
+ types::Value::Empty => {
+ *typ = ValueInnerType::EMPTY;
+ *content = null();
+ *len = 0;
+ }
+ types::Value::Int(c) => {
+ *typ = ValueInnerType::INT;
+ let ptr = &mut *(content as *mut c_int);
+ *ptr = *c as c_int;
+ }
+ types::Value::Double(c) => {
+ *typ = ValueInnerType::DOUBLE;
+ let ptr = &mut *(content as *mut c_double);
+ *ptr = *c;
+ }
+ types::Value::String(c) => {
+ *typ = ValueInnerType::STRING;
+ *content = c.as_ptr() as *const c_void;
+ *len = c.len();
+ }
+ types::Value::Bool(c) => {
+ *typ = ValueInnerType::BOOL;
+ let ptr = &mut *(content as *mut c_int);
+ *ptr = *c as c_int;
+ }
+ types::Value::Bytes(c) => {
+ *typ = ValueInnerType::BYTES;
+ *content = c.as_ptr() as *const c_void;
+ *len = c.len();
+ }
+ types::Value::Asset(c) => {
+ *typ = ValueInnerType::ASSET;
+ *content = c as *const asset_loader::Asset as *const c_void;
+ }
+ types::Value::Assets(c) => {
+ *typ = ValueInnerType::ASSETS;
+ let vec = VectorCffi::Asset(c.to_vec());
+ *content = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector;
+ *len = c.len();
+ }
+ }
+ SUCCESS
+}
+
+/// Free a Value pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueFree(src: *mut Value) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut types::Value);
+ }
+}
+
+unsafe fn get_asset(builder: *const AssetBuilder) -> asset_loader::Asset {
+ let builder = &*builder;
+ let id = char_ptr_to_string(builder.id, builder.id_len);
+ let name = char_ptr_to_string(builder.name, builder.name_len);
+ let uri = char_ptr_to_string(builder.uri, builder.uri_len);
+ let local_path = char_ptr_to_string(builder.local_path, builder.local_path_len);
+ let create_time = char_ptr_to_string(builder.create_time, builder.create_time_len);
+ let modify_time = char_ptr_to_string(builder.modify_time, builder.modify_time_len);
+ let size = char_ptr_to_string(builder.size, builder.size_len);
+ let hash = char_ptr_to_string(builder.hash, builder.hash_len);
+
+ asset_loader::Asset {
+ status: builder.status.clone(),
+ id,
+ name,
+ uri,
+ local_path,
+ create_time,
+ modify_time,
+ size,
+ hash,
+ }
+}
+
+/// Initialize an Asset by the AssetBuilder.
+#[no_mangle]
+pub unsafe extern "C" fn AssetNew(builder: *mut AssetBuilder) -> *mut Asset {
+ if builder.is_null() {
+ return null_mut();
+ }
+
+ let asset = get_asset(builder);
+ Box::into_raw(Box::new(asset)) as *mut Asset
+}
+
+/// Get id from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetId(
+ src: *const Asset,
+ id: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *id = src_struct.id.as_ptr() as *const c_uchar;
+ *len = src_struct.id.len();
+ SUCCESS
+}
+
+/// Get name from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetName(
+ src: *const Asset,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *name = src_struct.name.as_ptr() as *const c_uchar;
+ *len = src_struct.name.len();
+ SUCCESS
+}
+
+/// Get uri from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetUri(
+ src: *const Asset,
+ uri: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *uri = src_struct.uri.as_ptr() as *const c_uchar;
+ *len = src_struct.uri.len();
+ SUCCESS
+}
+
+/// Get local path from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetLocalPath(
+ src: *const Asset,
+ local_path: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *local_path = src_struct.local_path.as_ptr() as *const c_uchar;
+ *len = src_struct.local_path.len();
+ SUCCESS
+}
+
+/// Get create time from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetCreateTime(
+ src: *const Asset,
+ create_time: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *create_time = src_struct.create_time.as_ptr() as *const c_uchar;
+ *len = src_struct.create_time.len();
+ SUCCESS
+}
+
+/// Get modified time from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetModifiedTime(
+ src: *const Asset,
+ modify_time: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *modify_time = src_struct.modify_time.as_ptr() as *const c_uchar;
+ *len = src_struct.modify_time.len();
+ SUCCESS
+}
+
+/// Get size from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetSize(
+ src: *const Asset,
+ size: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *size = src_struct.size.as_ptr() as *const c_uchar;
+ *len = src_struct.size.len();
+ SUCCESS
+}
+
+/// Get hash from an Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetGetHash(
+ src: *const Asset,
+ hash: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if src.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let src_struct = &*(src as *const asset_loader::Asset);
+ *hash = src_struct.hash.as_ptr() as *const c_uchar;
+ *len = src_struct.hash.len();
+ SUCCESS
+}
+
+/// Free a Asset pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetFree(src: *mut Asset) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut asset_loader::Asset);
+ }
+}
+
+/// Create a Database instance by database name, alias, and tables. Tables can be created by
+/// basic_rust_types::HashMap, and its type should be HashMap. When passed in,
+/// tables don't need to be freed again, because their management will be transferred to Database
+/// instance.
+#[no_mangle]
+pub unsafe extern "C" fn DatabaseNew(
+ name: *const c_uchar,
+ name_len: usize,
+ alias: *const c_uchar,
+ alias_len: usize,
+ tables: *const basic_rust_types::HashMap,
+) -> *mut Database {
+ let name = char_ptr_to_string(name, name_len);
+ let alias = char_ptr_to_string(alias, alias_len);
+ let tables = *Box::from_raw(tables as *mut HashMapCffi);
+ match tables {
+ HashMapCffi::Table(t) => {
+ let db = types::Database::new(name, alias, t);
+ Box::into_raw(Box::new(db)) as *mut Database
+ }
+ _ => null_mut(),
+ }
+}
+
+/// Get name from a Database pointer.
+#[no_mangle]
+pub unsafe extern "C" fn DatabaseGetName(
+ db: *const Database,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if db.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let db_struct = &*(db as *const types::Database);
+ *name = db_struct.name.as_ptr() as *const c_uchar;
+ *len = db_struct.name.len();
+ SUCCESS
+}
+
+/// Get alias from a Database pointer.
+#[no_mangle]
+pub unsafe extern "C" fn DatabaseGetAlias(
+ db: *const Database,
+ alias: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if db.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let db_struct = &*(db as *const types::Database);
+ *alias = db_struct.alias.as_ptr() as *const c_uchar;
+ *len = db_struct.alias.len();
+ SUCCESS
+}
+
+/// Get hashmap of tables from a Database pointer. Parameter `tables` will be updated
+/// to hold the output HashMap, and it should be freed by `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn DatabaseGetTable(
+ db: *const Database,
+ tables: *mut *const basic_rust_types::HashMap,
+) -> c_int {
+ if db.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let db_struct = &*(db as *const types::Database);
+ let tb_hashmap = HashMapCffi::Table(db_struct.tables.clone());
+ *tables = Box::into_raw(Box::new(tb_hashmap)) as *mut basic_rust_types::HashMap;
+ SUCCESS
+}
+
+/// Free a Database pointer.
+#[no_mangle]
+pub unsafe extern "C" fn DatabaseFree(src: *mut Database) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut types::Database);
+ }
+}
+
+/// Create a Table instance by table name, table alias, and vector of fields. Fields can be created
+/// by basic_rust_types::Vector, and its type should be Vec. When passed in, fields don't
+/// need to be freed again, because their management will be transferred to Table instance.
+#[no_mangle]
+pub unsafe extern "C" fn TableNew(
+ name: *const c_uchar,
+ name_len: usize,
+ alias: *const c_uchar,
+ alias_len: usize,
+ fields: *const basic_rust_types::Vector,
+) -> *mut Table {
+ let name = char_ptr_to_string(name, name_len);
+ let alias = char_ptr_to_string(alias, alias_len);
+ let fields = *Box::from_raw(fields as *mut VectorCffi);
+ match fields {
+ VectorCffi::Field(f) => {
+ let tb = types::Table::new(name, alias, f);
+ Box::into_raw(Box::new(tb)) as *mut Table
+ }
+ _ => null_mut(),
+ }
+}
+
+/// Get name from a Table pointer.
+#[no_mangle]
+pub unsafe extern "C" fn TableGetName(
+ tb: *const Table,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if tb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let tb_struct = &*(tb as *const types::Table);
+ *name = tb_struct.name.as_ptr() as *const c_uchar;
+ *len = tb_struct.name.len();
+ SUCCESS
+}
+
+/// Get alias from a Table pointer.
+#[no_mangle]
+pub unsafe extern "C" fn TableGetAlias(
+ tb: *const Table,
+ alias: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if tb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let tb_struct = &*(tb as *const types::Table);
+ *alias = tb_struct.alias.as_ptr() as *const c_uchar;
+ *len = tb_struct.alias.len();
+ SUCCESS
+}
+
+/// Get vector of fields from a Table pointer. Parameter `fields` be updated
+/// to hold the output Vector, and it should be freed by `VectorFree`.
+#[no_mangle]
+pub unsafe extern "C" fn TableGetFields(
+ tb: *const Table,
+ fields: *mut *const basic_rust_types::Vector,
+) -> c_int {
+ if tb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let tb_struct = &*(tb as *const types::Table);
+ let vec = VectorCffi::Field(tb_struct.fields.clone());
+ *fields = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector;
+ SUCCESS
+}
+
+/// Free a Table pointer.
+#[no_mangle]
+pub unsafe extern "C" fn TableFree(src: *mut Table) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut types::Table);
+ }
+}
+
+#[inline]
+fn c_int_to_bool(src: c_int) -> bool {
+ src != 0
+}
+
+/// FieldBuilder providing necessary information when create a Field.
+#[repr(C)]
+pub struct FieldBuilder {
+ col_name: *const c_uchar,
+ col_name_len: usize,
+ alias: *const c_uchar,
+ alias_len: usize,
+ typ: c_uint,
+ primary: c_int,
+ nullable: c_int,
+}
+
+/// Create a Field by column name, alias, type, primary, and nullable.
+#[no_mangle]
+pub unsafe extern "C" fn FieldNew(builder: *const FieldBuilder) -> *mut Field {
+ let builder = &*builder;
+ let col_name = char_ptr_to_string(builder.col_name, builder.col_name_len);
+ let alias = char_ptr_to_string(builder.alias, builder.alias_len);
+ let typ = builder.typ as u8;
+ let primary = c_int_to_bool(builder.primary);
+ let nullable = c_int_to_bool(builder.nullable);
+ let fd = types::Field::new(col_name, alias, typ, primary, nullable);
+ Box::into_raw(Box::new(fd)) as *mut Field
+}
+
+/// Get column name from a Field pointer.
+#[no_mangle]
+pub unsafe extern "C" fn FieldGetColName(
+ fd: *const Field,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if fd.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let fd_struct = &*(fd as *const types::Field);
+ *name = fd_struct.col_name.as_ptr() as *const c_uchar;
+ *len = fd_struct.col_name.len();
+ SUCCESS
+}
+
+/// Get alias from a Field pointer.
+#[no_mangle]
+pub unsafe extern "C" fn FieldGetAlias(
+ fd: *const Field,
+ alias: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if fd.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let fd_struct = &*(fd as *const types::Field);
+ *alias = fd_struct.alias.as_ptr() as *const c_uchar;
+ *len = fd_struct.alias.len();
+ SUCCESS
+}
+
+/// Get type from a Field pointer.
+#[no_mangle]
+pub unsafe extern "C" fn FieldGetTyp(fd: *const Field, typ: *mut c_uint) -> c_int {
+ if fd.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let fd_struct = &*(fd as *const types::Field);
+ *typ = fd_struct.typ as c_uint;
+ SUCCESS
+}
+
+/// Check whether the Field is primary.
+#[no_mangle]
+pub unsafe extern "C" fn FieldGetPrimary(fd: *const Field, primary: *mut c_int) -> c_int {
+ if fd.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let fd_struct = &*(fd as *const types::Field);
+ *primary = fd_struct.primary as c_int;
+ SUCCESS
+}
+
+/// Check whether the Field is nullable.
+#[no_mangle]
+pub unsafe extern "C" fn FieldGetNullable(fd: *const Field, nullable: *mut c_int) -> c_int {
+ if fd.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let fd_struct = &*(fd as *const types::Field);
+ *nullable = fd_struct.nullable as c_int;
+ SUCCESS
+}
+
+/// Free a Field pointer.
+#[no_mangle]
+pub unsafe extern "C" fn FieldFree(src: *mut Field) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut types::Field);
+ }
+}
+
+/// Get user from a CloudInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoGetUser(info: *const CloudInfo, user: *mut c_int) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ *user = info_struct.user;
+ SUCCESS
+}
+
+/// Get id from a CloudInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoGetId(
+ info: *const CloudInfo,
+ id: *mut *const c_uchar,
+ id_len: *mut usize,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ *id = info_struct.id.as_ptr() as *const c_uchar;
+ *id_len = info_struct.id.len();
+ SUCCESS
+}
+
+/// Get total space from a CloudInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoGetTotalSpace(
+ info: *const CloudInfo,
+ total_space: *mut c_int,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ *total_space = info_struct.total_space as c_int;
+ SUCCESS
+}
+
+/// Get remain space from a CloudInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoGetRemainSpace(
+ info: *const CloudInfo,
+ remain_space: *mut c_int,
+) -> c_int {
+ if info.is_null() {
+ return false as c_int;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ *remain_space = info_struct.remain_space as c_int;
+ SUCCESS
+}
+
+/// Check whether a CloudInfo enables cloud sync.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoEnableCloud(info: *const CloudInfo, enable: *mut c_int) -> c_int {
+ if info.is_null() {
+ return false as c_int;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ *enable = info_struct.enable_cloud as c_int;
+ SUCCESS
+}
+
+/// Get hashmap of AppInfo from a CloudInfo pointer. Parameter `app_info` will be updated
+/// to hold the output HashMap, and it should be freed by `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoGetAppInfo(
+ info: *const CloudInfo,
+ app_info: *mut *const basic_rust_types::HashMap,
+) -> c_int {
+ if info.is_null() {
+ return false as c_int;
+ }
+
+ let info_struct = &*(info as *const cloud_service::CloudInfo);
+ let apps = HashMapCffi::AppInfo(info_struct.apps.clone());
+ *app_info = Box::into_raw(Box::new(apps)) as *mut basic_rust_types::HashMap;
+ SUCCESS
+}
+
+/// Free a CloudInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudInfoFree(info: *const CloudInfo) {
+ if !info.is_null() {
+ let _ = Box::from_raw(info as *mut cloud_service::CloudInfo);
+ }
+}
+
+/// Get app id from an AppInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AppInfoGetAppId(
+ info: *const AppInfo,
+ id: *mut *const c_uchar,
+ id_len: *mut usize,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *const cloud_service::AppInfo);
+ *id = info_struct.app_id.as_ptr() as *const c_uchar;
+ *id_len = info_struct.app_id.len();
+ SUCCESS
+}
+
+/// Get bundle name from an AppInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AppInfoGetBundleName(
+ info: *const AppInfo,
+ id: *mut *const c_uchar,
+ id_len: *mut usize,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *const cloud_service::AppInfo);
+ *id = info_struct.bundle_name.as_ptr() as *const c_uchar;
+ *id_len = info_struct.bundle_name.len();
+ SUCCESS
+}
+
+/// Check whether an AppInfo pointer allows cloud switch.
+#[no_mangle]
+pub unsafe extern "C" fn AppInfoGetCloudSwitch(info: *const AppInfo, switch: *mut c_int) -> c_int {
+ if info.is_null() {
+ return false as c_int;
+ }
+
+ let info_struct = &*(info as *const cloud_service::AppInfo);
+ *switch = info_struct.cloud_switch as c_int;
+ SUCCESS
+}
+
+/// Get instance id from an AppInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AppInfoGetInstanceId(info: *const AppInfo, id: *mut c_int) -> c_int {
+ if info.is_null() {
+ return false as c_int;
+ }
+
+ let info_struct = &*(info as *const cloud_service::AppInfo);
+ *id = info_struct.instance_id;
+ SUCCESS
+}
+
+/// Get version from a SchemaMeta pointer.
+#[no_mangle]
+pub unsafe extern "C" fn SchemaMataGetVersion(
+ schema: *mut SchemaMata,
+ version: *mut c_int,
+) -> c_int {
+ if schema.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let schema_struct = &mut *(schema as *mut cloud_service::SchemaMeta);
+ *version = schema_struct.version();
+ SUCCESS
+}
+
+/// Get bundle name from a SchemaMeta pointer.
+#[no_mangle]
+pub unsafe extern "C" fn SchemaMataGetBundleName(
+ schema: *mut SchemaMata,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if schema.is_null() {
+ return false as c_int;
+ }
+
+ let schema_struct = &*(schema as *const cloud_service::SchemaMeta);
+ *name = schema_struct.bundle_name().as_ptr() as *const c_uchar;
+ *len = schema_struct.bundle_name().len();
+ SUCCESS
+}
+
+/// Get a vector of databases from a SchemaMeta pointer. Parameter `dbs` be updated
+/// to hold the output Vector, and it should be freed by `VectorFree`.
+#[no_mangle]
+pub unsafe extern "C" fn SchemaMataGetDatabases(
+ schema: *mut SchemaMata,
+ dbs: *mut *const basic_rust_types::Vector,
+) -> c_int {
+ if schema.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let schema_struct = &*(schema as *const cloud_service::SchemaMeta);
+ let databases = schema_struct.databases();
+ let vec = VectorCffi::Database(databases.to_vec());
+ *dbs = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector;
+ SUCCESS
+}
+
+/// Free a SchemaMeta pointer.
+#[no_mangle]
+pub unsafe extern "C" fn SchemaMataFree(schema: *mut SchemaMata) {
+ if !schema.is_null() {
+ let _ = Box::from_raw(schema as *mut cloud_service::SchemaMeta);
+ }
+}
+
+/// Create a RelationSet instance by bundle name, and relations. Relations can be created by
+/// basic_rust_types::HashMap, and its type should be HashMap. When passed in,
+/// tables don't need to be freed again, because their management will be transferred to RelationSet
+/// instance.
+#[no_mangle]
+pub unsafe extern "C" fn RelationSetNew(
+ bundle_name: *const c_uchar,
+ len: usize,
+ expire_time: c_int,
+ relations: *mut basic_rust_types::HashMap,
+) -> *mut RelationSet {
+ let name = char_ptr_to_string(bundle_name, len);
+ let relations = *Box::from_raw(relations as *mut HashMapCffi);
+ match relations {
+ HashMapCffi::U32(re) => {
+ let relation = cloud_service::RelationSet::new(name, expire_time as u32, re);
+ Box::into_raw(Box::new(relation)) as *mut RelationSet
+ }
+ _ => null_mut(),
+ }
+}
+
+/// Get bundle name from a RelationSet pointer.
+#[no_mangle]
+pub unsafe extern "C" fn RelationSetGetBundleName(
+ relation: *mut RelationSet,
+ bundle_name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if relation.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let relation_struct = &*(relation as *mut cloud_service::RelationSet);
+ *bundle_name = relation_struct.bundle_name.as_ptr() as *const c_uchar;
+ *len = relation_struct.bundle_name.len();
+ SUCCESS
+}
+
+/// Get expire time from a RelationSet pointer.
+#[no_mangle]
+pub unsafe extern "C" fn RelationSetGetExpireTime(
+ relation: *mut RelationSet,
+ expire_time: *mut c_uint,
+) -> c_int {
+ if relation.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let relation_struct = &*(relation as *mut cloud_service::RelationSet);
+ *expire_time = relation_struct.expire_time as c_uint;
+ SUCCESS
+}
+
+/// Get relations from a RelationSet pointer. Parameter `relations` be updated
+/// to hold the output HashMap, and it should be freed by `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn RelationSetGetRelations(
+ relation: *mut RelationSet,
+ relations: *mut *const basic_rust_types::HashMap,
+) -> c_int {
+ if relation.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let relation_struct = &*(relation as *const cloud_service::RelationSet);
+ let res = relation_struct.relations();
+ let hashmap = HashMapCffi::U32(res.clone());
+ *relations = Box::into_raw(Box::new(hashmap)) as *const basic_rust_types::HashMap;
+ SUCCESS
+}
+
+/// Free a RelationSet pointer.
+#[no_mangle]
+pub unsafe extern "C" fn RelationSetFree(re: *mut RelationSet) {
+ if !re.is_null() {
+ let _ = Box::from_raw(re as *mut cloud_service::RelationSet);
+ }
+}
+
+/// Get the next cursor from a CloudData pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDataGetNextCursor(
+ data: *mut CloudData,
+ cursor: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if data.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let data_struct = &*(data as *mut cloud_db::CloudData);
+ *cursor = data_struct.next_cursor.as_ptr() as *const c_uchar;
+ *len = data_struct.next_cursor.len();
+ SUCCESS
+}
+
+/// Check whether a CloudData has more data.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDataGetHasMore(data: *mut CloudData, has_more: *mut c_int) -> c_int {
+ if data.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let data_struct = &*(data as *mut cloud_db::CloudData);
+ *has_more = data_struct.has_more as c_int;
+ SUCCESS
+}
+
+/// Get vector of values from a CloudData pointer. Parameter `values` will be updated
+/// to hold the output Vec, and it should be freed by `VectorFree`.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDataGetGetValues(
+ data: *mut CloudData,
+ values: *mut *const basic_rust_types::Vector,
+) -> c_int {
+ if data.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let data_struct = &*(data as *mut cloud_db::CloudData);
+ let vec = VectorCffi::ValueBucket(data_struct.values.clone());
+ *values = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector;
+ SUCCESS
+}
+
+/// Free a CloudData pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDataFree(src: *mut CloudData) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut cloud_db::CloudData);
+ }
+}
+
+/// Get id from a ValueBucket pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetId(
+ vb: *mut ValueBucket,
+ id: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *id = vb_struct.id.as_ptr() as *const c_uchar;
+ *len = vb_struct.id.len();
+ SUCCESS
+}
+
+/// Get type string from a ValueBucket pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetTyp(
+ vb: *mut ValueBucket,
+ typ: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *typ = vb_struct.typ.as_ptr() as *const c_uchar;
+ *len = vb_struct.typ.len();
+ SUCCESS
+}
+
+/// Get create info from a ValueBucket pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetCreateInfo(
+ vb: *mut ValueBucket,
+ info: *mut *const VBInfo,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *info = (&vb_struct.create_info) as *const cloud_db::VBInfo as *const VBInfo;
+ SUCCESS
+}
+
+/// Get modified info from a ValueBucket pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetModifiedInfo(
+ vb: *mut ValueBucket,
+ info: *mut *const VBInfo,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *info = (&vb_struct.modified_info) as *const cloud_db::VBInfo as *const VBInfo;
+ SUCCESS
+}
+
+/// Get hashmap of data from a ValueBucket pointer. Parameter `data` will be updated
+/// to hold the output HashMap, and it should be freed by `HashMapFree`.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetData(
+ vb: *mut ValueBucket,
+ data: *mut *const basic_rust_types::HashMap,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ let vec = HashMapCffi::Value(vb_struct.data.clone());
+ *data = Box::into_raw(Box::new(vec)) as *const basic_rust_types::HashMap;
+ SUCCESS
+}
+
+/// Check whether a ValueBucket is deleted.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketIsDelete(vb: *mut ValueBucket, deleted: *mut c_int) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *deleted = vb_struct.is_delete as c_int;
+ SUCCESS
+}
+
+/// Get version from a ValueBucket pointer.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketGetVersion(
+ vb: *mut ValueBucket,
+ version: *mut c_uint,
+) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *version = vb_struct.version;
+ SUCCESS
+}
+
+/// Check whether a ValueBucket is deleted.
+#[no_mangle]
+pub unsafe extern "C" fn ValueBucketIsNewCreate(vb: *mut ValueBucket, is_new: *mut c_int) -> c_int {
+ if vb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let vb_struct = &*(vb as *mut cloud_db::ValueBucket);
+ *is_new = vb_struct.is_new_create as c_int;
+ SUCCESS
+}
+
+/// Get time from a VBInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VBInfoGetTime(info: *mut VBInfo, time: *mut c_int) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *mut cloud_db::VBInfo);
+ *time = info_struct.time() as c_int;
+ SUCCESS
+}
+
+/// Get device name from a VBInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VBInfoGetDeviceName(
+ info: *mut VBInfo,
+ name: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *mut cloud_db::VBInfo);
+ *name = info_struct.device_name().as_ptr() as *const c_uchar;
+ *len = info_struct.device_name().len();
+ SUCCESS
+}
+
+/// Get app id from a VBInfo pointer.
+#[no_mangle]
+pub unsafe extern "C" fn VBInfoGetAppId(
+ info: *mut VBInfo,
+ id: *mut *const c_uchar,
+ len: *mut usize,
+) -> c_int {
+ if info.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let info_struct = &*(info as *mut cloud_db::VBInfo);
+ *id = info_struct.app_id().as_ptr() as *const c_uchar;
+ *len = info_struct.app_id().len();
+ SUCCESS
+}
diff --git a/src/cloud_extension/src/c_adapter/cloud_extension.rs b/src/cloud_extension/src/c_adapter/cloud_extension.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5f2f9ab5fa447197dd1cda3140be9e8be0a34eac
--- /dev/null
+++ b/src/cloud_extension/src/c_adapter/cloud_extension.rs
@@ -0,0 +1,572 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::c_adapter::basic_rust_types;
+use crate::c_adapter::basic_rust_types::{HashMapCffi, VectorCffi};
+use crate::c_adapter::cloud_ext_types::*;
+use crate::c_adapter::*;
+use crate::ipc_conn;
+use crate::service_impl::error::SyncError;
+use crate::service_impl::{asset_loader, cloud_db, cloud_service, types};
+use std::ffi::{c_int, c_uchar, c_uint, c_void};
+use std::io::ErrorKind;
+use std::ptr::null_mut;
+
+/// AssetLoader pointer passed to C side.
+pub type AssetLoader = c_void;
+/// CloudDatabase pointer passed to C side.
+pub type CloudDatabase = c_void;
+/// CloudServer pointer passed to C side.
+pub type CloudServer = c_void;
+
+fn map_ipc_err(e: &ipc_conn::Error) -> c_int {
+ match e {
+ ipc_conn::Error::CreateMsgParcelFailed
+ | ipc_conn::Error::WriteMsgParcelFailed
+ | ipc_conn::Error::GetProxyObjectFailed
+ | ipc_conn::Error::SendRequestFailed => ERRNO_NETWORK_ERROR,
+ ipc_conn::Error::ReadMsgParcelFailed | ipc_conn::Error::InvalidCloudStatus => {
+ ERRNO_OTHER_IPC_ERROR
+ }
+ ipc_conn::Error::UnlockFailed => ERRNO_UNLOCKED,
+ ipc_conn::Error::InvalidSpaceStatus => ERRNO_NO_SPACE_FOR_ASSET,
+ ipc_conn::Error::DownloadFailed => ERRNO_ASSET_DOWNLOAD_FAILURE,
+ ipc_conn::Error::UploadFailed => ERRNO_ASSET_UPLOAD_FAILURE,
+ }
+}
+
+fn map_single_sync_err(e: &SyncError) -> c_int {
+ match e {
+ SyncError::Unknown => ERRNO_UNKNOWN,
+ SyncError::NoSuchTableInDb => ERRNO_NO_SUCH_TABLE_IN_DB,
+ SyncError::NotAnAsset => ERRNO_INVALID_INPUT_TYPE,
+ SyncError::AssetDownloadFailure => ERRNO_ASSET_DOWNLOAD_FAILURE,
+ SyncError::AssetUploadFailure => ERRNO_ASSET_UPLOAD_FAILURE,
+ SyncError::SessionUnlocked => ERRNO_UNLOCKED,
+ SyncError::Unsupported => ERRNO_UNSUPPORTED,
+ SyncError::IPCError(ipc_e) => map_ipc_err(ipc_e),
+ // Not deal here to involve vec in every branch
+ SyncError::IPCErrors(_) => ERRNO_IPC_ERRORS,
+ SyncError::IO(io_e) => match io_e.kind() {
+ ErrorKind::ConnectionRefused
+ | ErrorKind::ConnectionReset
+ | ErrorKind::ConnectionAborted
+ | ErrorKind::NotConnected
+ | ErrorKind::AddrInUse
+ | ErrorKind::AddrNotAvailable
+ | ErrorKind::BrokenPipe
+ | ErrorKind::UnexpectedEof => ERRNO_NETWORK_ERROR,
+ ErrorKind::OutOfMemory => ERRNO_NO_SPACE_FOR_ASSET,
+ _ => ERRNO_OTHER_IO_ERROR,
+ },
+ }
+}
+
+/// Create an AssetLoader instance by bundle name and database pointer. This function only borrows
+/// database, so this pointer won't be taken by AssetLoader. Users should free it if the database
+/// is no longer in need.
+#[no_mangle]
+pub unsafe extern "C" fn AssetLoaderNew(
+ user_id: c_int,
+ bundle_name: *mut c_uchar,
+ name_len: usize,
+ db: *mut Database,
+) -> *mut AssetLoader {
+ if bundle_name.is_null() | bundle_name.is_null() | db.is_null() {
+ return null_mut();
+ }
+ let bundle_name = String::from_raw_parts(bundle_name, name_len, name_len);
+ let asset_loader =
+ asset_loader::AssetLoader::new(user_id, &bundle_name, &*(db as *mut types::Database));
+ Box::into_raw(Box::from(asset_loader)) as *mut AssetLoader
+}
+
+/// Information needed when upload or download assets through AssetLoader.
+#[repr(C)]
+pub struct UpDownloadInfo {
+ table_name: *const c_uchar,
+ table_name_len: usize,
+ gid: *const c_uchar,
+ gid_len: usize,
+ prefix: *const c_uchar,
+ prefix_len: usize,
+}
+
+/// Upload assets, with table name, gid, and prefix. This function only mutably borrows
+/// assets, so this pointer won't be taken by AssetLoader. Users should free it if the hashmap of
+/// assets is no longer in need.
+///
+/// Assets should be in type HashMap.
+#[no_mangle]
+pub unsafe extern "C" fn AssetLoaderUpload(
+ loader: *mut AssetLoader,
+ info: *const UpDownloadInfo,
+ assets: *mut basic_rust_types::HashMap,
+) -> c_int {
+ if loader.is_null() | info.is_null() | assets.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let info = &*info;
+ let loader = &*(loader as *mut asset_loader::AssetLoader);
+ let table_name = String::from_raw_parts(
+ info.table_name as *mut u8,
+ info.table_name_len,
+ info.table_name_len,
+ );
+ let gid = String::from_raw_parts(info.gid as *mut u8, info.gid_len, info.gid_len);
+ let prefix = String::from_raw_parts(info.prefix as *mut u8, info.prefix_len, info.prefix_len);
+ let assets = &mut *(assets as *mut HashMapCffi);
+ let assets = &mut (*match assets {
+ HashMapCffi::Value(re) => re,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ });
+ match loader.upload(&table_name, &gid, &prefix, assets) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Download assets, with table name, gid, and prefix. This function only mutably borrows
+/// assets, so this pointer won't be taken by AssetLoader. Users should free it if the hashmap of
+/// assets is no longer in need.
+///
+/// Assets should be in type HashMap.
+#[no_mangle]
+pub unsafe extern "C" fn AssetLoaderDownload(
+ loader: *mut AssetLoader,
+ info: *const UpDownloadInfo,
+ assets: *mut basic_rust_types::HashMap,
+) -> c_int {
+ if loader.is_null() | info.is_null() | assets.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let info = &*info;
+ let loader = &*(loader as *mut asset_loader::AssetLoader);
+ let table_name = String::from_raw_parts(
+ info.table_name as *mut u8,
+ info.table_name_len,
+ info.table_name_len,
+ );
+ let gid = String::from_raw_parts(info.gid as *mut u8, info.gid_len, info.gid_len);
+ let prefix = String::from_raw_parts(info.prefix as *mut u8, info.prefix_len, info.prefix_len);
+ let assets = &mut *(assets as *mut HashMapCffi);
+ let assets = &mut (*match assets {
+ HashMapCffi::Value(re) => re,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ });
+ match loader.download(&table_name, &gid, &prefix, assets) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Remove local asset. This function will use file system and remove a local file. This function
+/// only mutably borrows asset, so this pointer won't be taken by AssetLoader. Users should free
+/// it if the asset is no longer in need.
+#[no_mangle]
+pub unsafe extern "C" fn AssetLoaderRemoveLocalAssets(asset: *mut Asset) -> c_int {
+ if asset.is_null() {
+ return -1;
+ }
+ let asset = &*(asset as *mut asset_loader::Asset);
+ match asset_loader::AssetLoader::remove_local_assets(asset) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Free an AssetLoader pointer.
+#[no_mangle]
+pub unsafe extern "C" fn AssetLoaderFree(src: *mut AssetLoader) {
+ if !src.is_null() {
+ let _ = Box::from_raw(src as *mut asset_loader::AssetLoader);
+ }
+}
+
+/// Initialize a CloudDatabase instance with bundle name and database. The database passed in will
+/// be stored in CloudDatabase, so its management will be transferred and it should not be freed
+/// by the users.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbNew(
+ user_id: c_int,
+ bundle_name: *mut c_uchar,
+ name_len: usize,
+ database: *mut Database,
+) -> *mut CloudDatabase {
+ if database.is_null() | bundle_name.is_null() {
+ return null_mut();
+ }
+ let database = *Box::from_raw(database as *mut types::Database);
+ let bundle_name = String::from_raw_parts(bundle_name as *mut u8, name_len, name_len);
+
+ if let Ok(db) = cloud_db::CloudDatabase::new(user_id, &bundle_name, database) {
+ return Box::into_raw(Box::from(db)) as *mut CloudDatabase;
+ }
+ null_mut()
+}
+
+/// Sql that will passed to CloudDatabase and executed.
+#[repr(C)]
+pub struct Sql {
+ table: *mut c_uchar,
+ table_len: usize,
+ sql: *mut c_uchar,
+ sql_len: usize,
+}
+
+/// Execute sql on a CloudDatabase.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbExecuteSql(
+ cdb: *mut CloudDatabase,
+ sql: *const Sql,
+ extend: *mut basic_rust_types::HashMap,
+) -> c_int {
+ if cdb.is_null() | sql.is_null() | extend.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let sql = &*sql;
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ let table = String::from_raw_parts(sql.table as *mut u8, sql.table_len, sql.table_len);
+ let sql = String::from_raw_parts(sql.sql as *mut u8, sql.sql_len, sql.sql_len);
+
+ let extend = &mut *(extend as *mut HashMapCffi);
+ let extend = &mut (*match extend {
+ HashMapCffi::Value(re) => re,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ });
+ match cdb.execute(&table, &sql, extend) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Insert records into a CloudDatabase, with table name, and two Vector of HashMap.
+/// Those two vectors passed in are only borrowed, and they should be freed by `VectorFree` if they
+/// are no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbBatchInsert(
+ cdb: *mut CloudDatabase,
+ table: *mut c_uchar,
+ table_len: usize,
+ value: *const basic_rust_types::Vector,
+ extend: *mut basic_rust_types::Vector,
+) -> c_int {
+ if cdb.is_null() | table.is_null() | value.is_null() | extend.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ let table = String::from_raw_parts(table as *mut u8, table_len, table_len);
+
+ let value = &mut *(value as *mut VectorCffi);
+ let value_vec = match value {
+ VectorCffi::HashMapValue(val) => val,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+ let extend = &mut *(extend as *mut VectorCffi);
+ let extend_vec = match extend {
+ VectorCffi::HashMapValue(val) => val,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+ match cdb.batch_insert(&table, value_vec, extend_vec) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Update records in a CloudDatabase, with table name, and two Vector of HashMap.
+/// Those two vectors passed in are only borrowed, and they should be freed by `VectorFree` if they
+/// are no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbBatchUpdate(
+ cdb: *mut CloudDatabase,
+ table: *mut c_uchar,
+ table_len: usize,
+ value: *const basic_rust_types::Vector,
+ extend: *mut basic_rust_types::Vector,
+) -> c_int {
+ if cdb.is_null() | table.is_null() | value.is_null() | extend.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ let table = String::from_raw_parts(table as *mut u8, table_len, table_len);
+
+ let value = &mut *(value as *mut VectorCffi);
+ let value_vec = match value {
+ VectorCffi::HashMapValue(val) => val,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+ let extend = &mut *(extend as *mut VectorCffi);
+ let extend_vec = match extend {
+ VectorCffi::HashMapValue(val) => val,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+
+ match cdb.batch_update(&table, value_vec, extend_vec) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Delete records from a CloudDatabase, with table name, and a Vector of HashMap.
+/// The vector passed in are only borrowed, and it should be freed by `VectorFree` if it
+/// is no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbBatchDelete(
+ cdb: *mut CloudDatabase,
+ table: *mut c_uchar,
+ table_len: usize,
+ extend: *mut basic_rust_types::Vector,
+) -> c_int {
+ if cdb.is_null() | table.is_null() | extend.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ let table = String::from_raw_parts(table as *mut u8, table_len, table_len);
+
+ let extend = &mut *(extend as *mut VectorCffi);
+ let extend_vec = match extend {
+ VectorCffi::HashMapValue(val) => val,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ };
+
+ match cdb.batch_delete(&table, extend_vec) {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Query info that will passed to CloudDatabase.
+#[repr(C)]
+pub struct QueryInfo {
+ table: *mut c_uchar,
+ table_len: usize,
+ cursor: *mut c_uchar,
+ cursor_len: usize,
+}
+
+/// Query records from a CloudDatabase, with table name and cursor. Return a CloudData pointer, which
+/// should be freed by `CloudDataFree` if it's no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbBatchQuery(
+ cdb: *mut CloudDatabase,
+ info: *const QueryInfo,
+ out: *mut *const CloudData,
+) -> c_int {
+ if cdb.is_null() | info.is_null() {
+ return -1;
+ }
+ let info = &*info;
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ let table = String::from_raw_parts(info.table as *mut u8, info.table_len, info.table_len);
+ let cursor = String::from_raw_parts(info.cursor as *mut u8, info.cursor_len, info.cursor_len);
+
+ match cdb.batch_query(&table, &cursor) {
+ Ok(cloud_data) => {
+ *out = Box::into_raw(Box::new(cloud_data)) as *const CloudData;
+ SUCCESS
+ }
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Lock a CloudDatabase. Return expire time.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbLock(cdb: *mut CloudDatabase, expire: *mut c_int) -> c_int {
+ if cdb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ match cdb.lock() {
+ Ok(time) => {
+ *expire = time;
+ SUCCESS
+ }
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Unlock a CloudDatabase.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbUnlock(cdb: *mut CloudDatabase) -> c_int {
+ if cdb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ match cdb.unlock() {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Heartbeat function of a CloudDatabase. Extend the current locking session.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDbHeartbeat(cdb: *mut CloudDatabase) -> c_int {
+ if cdb.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase);
+ match cdb.heartbeat() {
+ Ok(()) => SUCCESS,
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Free a CloudDatabase pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudDatabaseFree(cdb: *mut CloudDatabase) {
+ if !cdb.is_null() {
+ let _ = Box::from_raw(cdb as *mut cloud_db::CloudDatabase);
+ }
+}
+
+/// Create a CloudServer instance by user id.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerNew(user_id: c_int) -> *mut CloudServer {
+ if let Ok(service) = cloud_service::CloudServer::new(user_id) {
+ return Box::into_raw(Box::from(service)) as *mut CloudServer;
+ }
+ null_mut()
+}
+
+/// Get service info from a CloudServer pointer. Return a CloudInfo pointer, which
+/// should be freed by `CloudInfoFree` if it's no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerGetServiceInfo(
+ server: *mut CloudServer,
+ info: *mut *const CloudInfo,
+) -> c_int {
+ if server.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cloud_server = &mut *(server as *mut cloud_service::CloudServer);
+ match cloud_server.get_service_info() {
+ Ok(cloud_info) => {
+ *info = Box::into_raw(Box::new(cloud_info)) as *const CloudInfo;
+ SUCCESS
+ }
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Get app schema from a CloudServer pointer, with a bundle name. Return a SchemaMata pointer, which
+/// should be freed by `SchemaMataFree` if it's no longer in use.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerGetAppSchema(
+ server: *mut CloudServer,
+ bundle_name: *mut c_uchar,
+ bundle_name_len: usize,
+ schema: *mut *const SchemaMata,
+) -> c_int {
+ if server.is_null() | bundle_name.is_null() {
+ return ERRNO_NULLPTR;
+ }
+ let cloud_server = &mut *(server as *mut cloud_service::CloudServer);
+ let bundle_name = String::from_raw_parts(bundle_name, bundle_name_len, bundle_name_len);
+ match cloud_server.get_app_schema(&bundle_name) {
+ Ok(schema_meta) => {
+ *schema = Box::into_raw(Box::new(schema_meta)) as *const SchemaMata;
+ SUCCESS
+ }
+ Err(e) => map_single_sync_err(&e),
+ }
+}
+
+/// Pass a batch of subscription orders to a CloudServer pointer, with target database information
+/// and expected expire time. Database information should be in type HashMap>.
+/// Return a Hashmap of subscription relations, in the form HashMap, and Vector
+/// of errors in c_int, if failure happens. The Hashmap and Vector returned should be freed by
+/// `HashMapFree` and `VectorFree` respectively, if not in use anymore.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerSubscribe(
+ server: *mut CloudServer,
+ dbs: *const basic_rust_types::HashMap,
+ expire: c_uint,
+ relations: *mut *const basic_rust_types::HashMap,
+ err: *mut *const basic_rust_types::Vector,
+) -> c_int {
+ if server.is_null() | dbs.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let cloud_server = &mut *(server as *mut cloud_service::CloudServer);
+
+ let dbs = &mut *(dbs as *mut HashMapCffi);
+ let dbs = &mut (*match dbs {
+ HashMapCffi::VecDatabase(databases) => databases,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ });
+
+ match cloud_server.subscribe(dbs, expire) {
+ Ok(ret) => {
+ let result = HashMapCffi::RelationSet(ret);
+ *relations = Box::into_raw(Box::new(result)) as *const basic_rust_types::HashMap;
+ *err = null_mut();
+ SUCCESS
+ }
+ Err(e) => {
+ let errno = map_single_sync_err(&e);
+ if errno == ERRNO_IPC_ERRORS {
+ if let SyncError::IPCErrors(vec) = e {
+ let ret = VectorCffi::I32(vec.iter().map(map_ipc_err).collect());
+ *err = Box::into_raw(Box::new(ret)) as *const basic_rust_types::Vector;
+ }
+ }
+ errno
+ }
+ }
+}
+
+/// Pass a batch of unsubscription orders to a CloudServer pointer, with target relations.
+/// Relations should be in type HashMap>, with bundle name as keys, vector of
+/// subscription ids as value. Return a Vector of errors in c_int, if failure happens. The Vector
+/// returned should be freed by `VectorFree` respectively, if not in use anymore.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerUnsubscribe(
+ server: *mut CloudServer,
+ relations: *const basic_rust_types::HashMap,
+ err: *mut *const basic_rust_types::Vector,
+) -> c_int {
+ if server.is_null() | relations.is_null() {
+ return ERRNO_NULLPTR;
+ }
+
+ let cloud_server = &mut *(server as *mut cloud_service::CloudServer);
+
+ let relations = &mut *(relations as *mut HashMapCffi);
+ let relations = &mut (*match relations {
+ HashMapCffi::VecU32(res) => res,
+ _ => return ERRNO_INVALID_INPUT_TYPE,
+ });
+
+ match cloud_server.unsubscribe(relations) {
+ Ok(()) => SUCCESS,
+ Err(e) => {
+ let errno = map_single_sync_err(&e);
+ if errno == ERRNO_IPC_ERRORS {
+ if let SyncError::IPCErrors(vec) = e {
+ let ret = VectorCffi::I32(vec.iter().map(map_ipc_err).collect());
+ *err = Box::into_raw(Box::new(ret)) as *const basic_rust_types::Vector;
+ }
+ }
+ errno
+ }
+ }
+}
+
+/// Free a CloudServer pointer.
+#[no_mangle]
+pub unsafe extern "C" fn CloudServerFree(server: *mut CloudServer) {
+ if !server.is_null() {
+ let _ = Box::from_raw(server as *mut cloud_service::CloudServer);
+ }
+}
diff --git a/src/cloud_extension/src/c_adapter/mod.rs b/src/cloud_extension/src/c_adapter/mod.rs
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9aecf05b79d6cb538d72a72446a05b2a59a85074 100644
--- a/src/cloud_extension/src/c_adapter/mod.rs
+++ b/src/cloud_extension/src/c_adapter/mod.rs
@@ -0,0 +1,62 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+pub(crate) mod basic_rust_types;
+pub(crate) mod cloud_ext_types;
+pub(crate) mod cloud_extension;
+
+use std::ffi::{c_int, c_uchar};
+use std::ptr::slice_from_raw_parts;
+
+/// Errno of success.
+pub const SUCCESS: c_int = 0;
+/// Errno of null pointer.
+pub const ERRNO_NULLPTR: c_int = 1;
+/// Errno of unknown error.
+pub const ERRNO_UNKNOWN: c_int = 2;
+/// Errno of invalid input type.
+pub const ERRNO_INVALID_INPUT_TYPE: c_int = 3;
+/// Errno of asset download failure.
+pub const ERRNO_ASSET_DOWNLOAD_FAILURE: c_int = 4;
+/// Errno of asset upload failure.
+pub const ERRNO_ASSET_UPLOAD_FAILURE: c_int = 5;
+/// Errno of unsupported feature.
+pub const ERRNO_UNSUPPORTED: c_int = 6;
+/// Errno of network error.
+pub const ERRNO_NETWORK_ERROR: c_int = 7;
+/// Errno of lock conflict.
+pub const ERRNO_LOCKED_BY_OTHERS: c_int = 8;
+/// Errno of locking action on unlocked cloud database.
+pub const ERRNO_UNLOCKED: c_int = 9;
+/// Errno of record limit exceeding.
+pub const ERRNO_RECORD_LIMIT_EXCEEDED: c_int = 10;
+/// Errno of no space for assets.
+pub const ERRNO_NO_SPACE_FOR_ASSET: c_int = 11;
+/// Errno of other ipc error.
+pub const ERRNO_OTHER_IPC_ERROR: c_int = 12;
+/// Errno of batch of ipc errors.
+pub const ERRNO_IPC_ERRORS: c_int = 13;
+/// Errno of other io error.
+pub const ERRNO_OTHER_IO_ERROR: c_int = 14;
+/// Errno of array out of range.
+pub const ERRNO_OUT_OF_RANGE: c_int = 15;
+/// Errno of no target table in source database.
+pub const ERRNO_NO_SUCH_TABLE_IN_DB: c_int = 16;
+
+pub(crate) unsafe fn char_ptr_to_string(ptr: *const c_uchar, len: usize) -> String {
+ if ptr.is_null() {
+ return String::default();
+ }
+ let slice = &(*slice_from_raw_parts(ptr as *const u8, len));
+ String::from_utf8_unchecked(slice.to_vec())
+}
diff --git a/src/cloud_extension/src/ipc_conn/asset.rs b/src/cloud_extension/src/ipc_conn/asset.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ba192ccd339ebdd2756673285ec15cb1306c93d5
--- /dev/null
+++ b/src/cloud_extension/src/ipc_conn/asset.rs
@@ -0,0 +1,562 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::ipc_conn::error::Error;
+use crate::ipc_conn::ffi::ConnectService;
+use crate::ipc_conn::function::AssetLoaderFunc::{ConnectAssetLoader, Download, Upload};
+use crate::ipc_conn::{vec_raw_read, vec_raw_write};
+use ipc_rust::{
+ BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcStatusCode, MsgParcel, RemoteObj,
+ Serialize,
+};
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum OperationType {
+ None,
+ Add,
+ Update,
+ Delete,
+}
+
+impl Default for OperationType {
+ fn default() -> Self {
+ Self::None
+ }
+}
+
+impl Serialize for OperationType {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ match self {
+ OperationType::None => parcel.write(&0_u32),
+ OperationType::Add => parcel.write(&1_u32),
+ OperationType::Update => parcel.write(&2_u32),
+ OperationType::Delete => parcel.write(&3_u32),
+ }
+ }
+}
+
+impl Deserialize for OperationType {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let index = parcel.read::()?;
+ match index {
+ 0 => Ok(OperationType::None),
+ 1 => Ok(OperationType::Add),
+ 2 => Ok(OperationType::Update),
+ 3 => Ok(OperationType::Delete),
+ _ => Err(IpcStatusCode::Failed),
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum SwitchStatus {
+ Close,
+ Open,
+ NotEnable,
+}
+
+impl Default for SwitchStatus {
+ fn default() -> Self {
+ Self::Close
+ }
+}
+
+impl Serialize for SwitchStatus {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ match self {
+ SwitchStatus::Close => parcel.write(&0_u32),
+ SwitchStatus::Open => parcel.write(&1_u32),
+ SwitchStatus::NotEnable => parcel.write(&2_u32),
+ }
+ }
+}
+
+impl Deserialize for SwitchStatus {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let index = parcel.read::()?;
+ match index {
+ 0 => Ok(SwitchStatus::Close),
+ 1 => Ok(SwitchStatus::Open),
+ 2 => Ok(SwitchStatus::NotEnable),
+ _ => Err(IpcStatusCode::Failed),
+ }
+ }
+}
+
+#[derive(Default, PartialEq)]
+pub(crate) struct Asset {
+ pub(crate) remote_obj: Option,
+ pub(crate) uri: String,
+ pub(crate) asset_name: String,
+ pub(crate) operation_type: OperationType,
+ pub(crate) hash: String,
+ pub(crate) version: u64,
+ pub(crate) asset_id: String,
+ pub(crate) sub_path: String,
+ pub(crate) prefix: String,
+ pub(crate) alias: String,
+ pub(crate) gid: String,
+ pub(crate) switch_status: SwitchStatus,
+}
+
+impl Serialize for Asset {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ parcel.write(&self.uri)?;
+ parcel.write(&self.asset_name)?;
+ parcel.write(&self.operation_type)?;
+ parcel.write(&self.hash)?;
+ parcel.write(&self.version)?;
+ parcel.write(&self.asset_id)?;
+ parcel.write(&self.sub_path)?;
+ parcel.write(&self.prefix)?;
+ parcel.write(&self.alias)?;
+ parcel.write(&self.gid)?;
+ parcel.write(&self.switch_status)?;
+ Ok(())
+ }
+}
+
+impl Deserialize for Asset {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let uri = parcel.read::()?;
+ let asset_name = parcel.read::()?;
+ let operation_type = parcel.read::()?;
+ let hash = parcel.read::()?;
+ let version = parcel.read::()?;
+ let asset_id = parcel.read::()?;
+ let sub_path = parcel.read::()?;
+ let prefix = parcel.read::()?;
+ let alias = parcel.read::()?;
+ let gid = parcel.read::()?;
+ let switch_status = parcel.read::()?;
+
+ let result = Asset {
+ remote_obj: None,
+ uri,
+ asset_name,
+ operation_type,
+ hash,
+ version,
+ asset_id,
+ sub_path,
+ prefix,
+ alias,
+ gid,
+ switch_status,
+ };
+ Ok(result)
+ }
+}
+
+impl Asset {
+ pub(crate) fn new(user_id: i32) -> Result {
+ let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+ let function_number = ConnectAssetLoader as u32;
+ let remote_obj =
+ unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) }
+ .ok_or(Error::GetProxyObjectFailed)?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+
+ let mut result = receive
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ result.remote_obj = Some(remote_obj);
+ Ok(result)
+ }
+
+ // Responsible for telling the cloud to perform file downloads.
+ pub(crate) fn download(&self, table: &str) -> Result<(), Error> {
+ let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+ msg_parcel
+ .write(table)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+ msg_parcel
+ .write(&self)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+
+ let function_number = Download as u32;
+ let remote_obj = self
+ .remote_obj
+ .as_ref()
+ .ok_or(Error::GetProxyObjectFailed)?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+
+ let result = receive
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ if !result {
+ return Err(Error::DownloadFailed);
+ }
+ Ok(())
+ }
+
+ // Responsible for telling the cloud to perform file uploads.
+ pub(crate) fn upload(&self, table: &str) -> Result<(), Error> {
+ let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+ msg_parcel
+ .write(table)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+ msg_parcel
+ .write(&self)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+
+ let function_number = Upload as u32;
+ let remote_obj = self
+ .remote_obj
+ .as_ref()
+ .ok_or(Error::GetProxyObjectFailed)?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+
+ let result = receive
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ if !result {
+ return Err(Error::UploadFailed);
+ }
+ Ok(())
+ }
+}
+
+#[derive(Default, PartialEq)]
+pub(crate) struct Assets(pub(crate) Vec);
+
+impl Serialize for Assets {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ vec_raw_write(parcel, &self.0)
+ }
+}
+
+impl Deserialize for Assets {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = Assets(vec_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[cfg(all(test))]
+mod test {
+ use crate::ipc_conn::asset::SwitchStatus;
+ use crate::ipc_conn::{Asset, Assets, OperationType};
+ use ipc_rust::MsgParcel;
+
+ /// UT test for operation_type_serialize.
+ ///
+ /// # Title
+ /// ut_operation_type_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `OperationType` enum.
+ /// 2. Serialize the enum.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_operation_type_serialize() {
+ let none = OperationType::None;
+ let add = OperationType::Add;
+ let update = OperationType::Update;
+ let delete = OperationType::Delete;
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&none).unwrap();
+ msg_parcel.write(&add).unwrap();
+ msg_parcel.write(&update).unwrap();
+ msg_parcel.write(&delete).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), 0_u32);
+ assert_eq!(msg_parcel.read::().unwrap(), 1_u32);
+ assert_eq!(msg_parcel.read::().unwrap(), 2_u32);
+ assert_eq!(msg_parcel.read::().unwrap(), 3_u32);
+ }
+
+ /// UT test for operation_type_deserialize.
+ ///
+ /// # Title
+ /// ut_operation_type_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `OperationType` enum.
+ /// 2. Serialize the enum.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `OperationType`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_operation_type_deserialize() {
+ let none = OperationType::None;
+ let add = OperationType::Add;
+ let update = OperationType::Update;
+ let delete = OperationType::Delete;
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&none).unwrap();
+ msg_parcel.write(&add).unwrap();
+ msg_parcel.write(&update).unwrap();
+ msg_parcel.write(&delete).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), none);
+ assert_eq!(msg_parcel.read::().unwrap(), add);
+ assert_eq!(msg_parcel.read::().unwrap(), update);
+ assert_eq!(msg_parcel.read::().unwrap(), delete);
+ }
+
+ /// UT test for switch_status_serialize.
+ ///
+ /// # Title
+ /// ut_switch_status_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `SwitchStatus` enum.
+ /// 2. Serialize the enum.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_switch_status_serialize() {
+ let close = SwitchStatus::Close;
+ let open = SwitchStatus::Open;
+ let not_enable = SwitchStatus::NotEnable;
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&close).unwrap();
+ msg_parcel.write(&open).unwrap();
+ msg_parcel.write(¬_enable).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), 0_u32);
+ assert_eq!(msg_parcel.read::().unwrap(), 1_u32);
+ assert_eq!(msg_parcel.read::().unwrap(), 2_u32);
+ }
+
+ /// UT test for switch_status_deserialize.
+ ///
+ /// # Title
+ /// ut_switch_status_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `SwitchStatus` enum.
+ /// 2. Serialize the enum.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `SwitchStatus`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_switch_status_deserialize() {
+ let close = SwitchStatus::Close;
+ let open = SwitchStatus::Open;
+ let not_enable = SwitchStatus::NotEnable;
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&close).unwrap();
+ msg_parcel.write(&open).unwrap();
+ msg_parcel.write(¬_enable).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), close);
+ assert_eq!(msg_parcel.read::().unwrap(), open);
+ assert_eq!(msg_parcel.read::().unwrap(), not_enable);
+ }
+
+ /// UT test for asset_serialize.
+ ///
+ /// # Title
+ /// ut_asset_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `Asset` struct.
+ /// 2. Serialize the struct.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_asset_serialize() {
+ let asset = Asset::default();
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&asset).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ OperationType::None
+ );
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), 0_u64);
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(msg_parcel.read::().unwrap(), String::from(""));
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ SwitchStatus::Close
+ );
+ }
+
+ /// UT test for asset_deserialize.
+ ///
+ /// # Title
+ /// ut_asset_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `Asset` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `Asset`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_asset_deserialize() {
+ let asset = Asset::default();
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&asset).unwrap();
+
+ let result = msg_parcel.read::().unwrap();
+ assert_eq!(result.uri, asset.uri);
+ assert_eq!(result.asset_name, asset.asset_name);
+ assert_eq!(result.operation_type, asset.operation_type);
+ assert_eq!(result.hash, asset.hash);
+ assert_eq!(result.version, asset.version);
+ assert_eq!(result.asset_id, asset.asset_id);
+ assert_eq!(result.sub_path, asset.sub_path);
+ assert_eq!(result.prefix, asset.prefix);
+ assert_eq!(result.alias, asset.alias);
+ assert_eq!(result.gid, asset.gid);
+ assert_eq!(result.switch_status, asset.switch_status);
+ }
+
+ /// UT test for assets_serialize.
+ ///
+ /// # Title
+ /// ut_assets_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `Assets` struct.
+ /// 2. Serialize the struct.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_assets_serialize() {
+ let mut assets = Assets::default();
+ let asset_one = Asset::default();
+ let asset_two = Asset::default();
+ assets.0.push(asset_one);
+ assets.0.push(asset_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&assets).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), 2);
+
+ let default_one = Asset::default();
+ let result_one = msg_parcel.read::().unwrap();
+ assert_eq!(result_one.uri, default_one.uri);
+ assert_eq!(result_one.asset_name, default_one.asset_name);
+ assert_eq!(result_one.operation_type, default_one.operation_type);
+ assert_eq!(result_one.hash, default_one.hash);
+ assert_eq!(result_one.version, default_one.version);
+ assert_eq!(result_one.asset_id, default_one.asset_id);
+ assert_eq!(result_one.sub_path, default_one.sub_path);
+ assert_eq!(result_one.prefix, default_one.prefix);
+ assert_eq!(result_one.alias, default_one.alias);
+ assert_eq!(result_one.gid, default_one.gid);
+ assert_eq!(result_one.switch_status, default_one.switch_status);
+
+ let default_two = Asset::default();
+ let result_two = msg_parcel.read::().unwrap();
+ assert_eq!(result_two.uri, default_two.uri);
+ assert_eq!(result_two.asset_name, default_two.asset_name);
+ assert_eq!(result_two.operation_type, default_two.operation_type);
+ assert_eq!(result_two.hash, default_two.hash);
+ assert_eq!(result_two.version, default_two.version);
+ assert_eq!(result_two.asset_id, default_two.asset_id);
+ assert_eq!(result_two.sub_path, default_two.sub_path);
+ assert_eq!(result_two.prefix, default_two.prefix);
+ assert_eq!(result_two.alias, default_two.alias);
+ assert_eq!(result_two.gid, default_two.gid);
+ assert_eq!(result_two.switch_status, default_two.switch_status);
+ }
+
+ /// UT test for assets_deserialize.
+ ///
+ /// # Title
+ /// ut_assets_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `Assets` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `Assets`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_assets_deserialize() {
+ let mut assets = Assets::default();
+ let asset_one = Asset::default();
+ let asset_two = Asset::default();
+ assets.0.push(asset_one);
+ assets.0.push(asset_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&assets).unwrap();
+
+ assert!(msg_parcel.read::().is_ok());
+ }
+
+ /// UT test for asset_new.
+ ///
+ /// # Title
+ /// ut_asset_new
+ ///
+ /// # Brief
+ /// 1. Create a `Asset` struct.
+ /// 2. Get self info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_asset_new() {
+ let user_id = 100;
+ let mut asset = Asset::new(user_id).unwrap();
+ assert!(asset.remote_obj.is_none());
+ }
+
+ /// UT test for asset_new.
+ ///
+ /// # Title
+ /// ut_asset_new
+ ///
+ /// # Brief
+ /// 1. Create a `Asset` struct.
+ /// 2. Download info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_asset_download() {
+ let user_id = 100;
+ let mut asset = Asset::new(user_id).unwrap();
+ let table = "";
+ let result = asset.download(table);
+ assert!(result.is_ok());
+ }
+
+ /// UT test for asset_new.
+ ///
+ /// # Title
+ /// ut_asset_new
+ ///
+ /// # Brief
+ /// 1. Create a `Asset` struct.
+ /// 2. Upload info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_asset_upload() {
+ let user_id = 100;
+ let mut asset = Asset::new(user_id).unwrap();
+ let table = "";
+ let result = asset.upload(table);
+ assert!(result.is_ok());
+ }
+}
diff --git a/src/cloud_extension/src/ipc_conn/connect.rs b/src/cloud_extension/src/ipc_conn/connect.rs
index 535265a51912d7bd08851724ab9d1eb9f62fccf8..62880d6ff95f69148d4972f818836fa268d321c1 100644
--- a/src/cloud_extension/src/ipc_conn/connect.rs
+++ b/src/cloud_extension/src/ipc_conn/connect.rs
@@ -11,180 +11,1237 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::ipc_conn::error::{Error, Errors};
+use crate::ipc_conn::ffi::ConnectService;
+use crate::ipc_conn::function::CloudServiceFunc::{
+ GetAppBriefInfo, GetAppSchema, GetServiceInfo, Subscribe, Unsubscribe,
+};
+use crate::ipc_conn::{
+ hash_map_raw_read, hash_map_raw_write, vec_raw_read, vec_raw_write, Database,
+};
+use ipc_rust::{
+ BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcResult, IpcStatusCode, MsgParcel,
+ RemoteObj, Serialize,
+};
use std::collections::HashMap;
+use std::sync::{RwLock, RwLockReadGuard};
-pub struct AppInfo {
- app_bundle_name: String,
- app_id: String,
- app_fingerprint: String,
- // And more...
+/************** All AppSchema struct-related structures and methods **************/
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum FieldType {
+ Null,
+ Number,
+ Real,
+ Text,
+ Bool,
+ Blob,
+ Asset,
+ Assets,
}
-pub enum CloudStatus {
- Unknown,
- UnActivated,
- Login,
- Logout,
+impl Serialize for FieldType {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> {
+ match self {
+ FieldType::Null => parcel.write(&0_u32),
+ FieldType::Number => parcel.write(&1_u32),
+ FieldType::Real => parcel.write(&2_u32),
+ FieldType::Text => parcel.write(&3_u32),
+ FieldType::Bool => parcel.write(&4_u32),
+ FieldType::Blob => parcel.write(&5_u32),
+ FieldType::Asset => parcel.write(&6_u32),
+ FieldType::Assets => parcel.write(&7_u32),
+ }
+ }
}
-pub enum SpaceStatus {
- Normal,
- Full,
+
+impl Deserialize for FieldType {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult {
+ let index = parcel.read::()?;
+ match index {
+ 0 => Ok(FieldType::Null),
+ 1 => Ok(FieldType::Number),
+ 2 => Ok(FieldType::Real),
+ 3 => Ok(FieldType::Text),
+ 4 => Ok(FieldType::Bool),
+ 5 => Ok(FieldType::Blob),
+ 6 => Ok(FieldType::Asset),
+ 7 => Ok(FieldType::Assets),
+ _ => Err(IpcStatusCode::Failed),
+ }
+ }
}
-pub struct UserInfo {
- account_id: String,
- cloud_status: CloudStatus,
- space_status: SpaceStatus,
- total_space: u64,
- left_space: u64,
- // And more...
+
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct Field {
+ pub(crate) col_name: String,
+ pub(crate) alias: String,
+ pub(crate) typ: FieldType,
+ pub(crate) primary: bool,
+ pub(crate) nullable: bool,
}
-pub struct Connect {
- infos: HashMap,
+impl Serialize for Field {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> {
+ parcel.write(&self.col_name)?;
+ parcel.write(&self.alias)?;
+ parcel.write(&self.typ)?;
+ parcel.write(&self.primary)?;
+ parcel.write(&self.nullable)?;
+ Ok(())
+ }
}
-impl Connect {
- pub fn new() -> Self {
- todo!()
+impl Deserialize for Field {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult {
+ let col_name = parcel.read::()?;
+ let cloud_name = parcel.read::()?;
+ let typ = parcel.read::()?;
+ let primary = parcel.read::()?;
+ let nullable = parcel.read::()?;
+
+ let result = Field {
+ col_name,
+ alias: cloud_name,
+ typ,
+ primary,
+ nullable,
+ };
+ Ok(result)
}
+}
+
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct Fields(pub(crate) Vec);
- pub fn get(user_id: i32) -> Infos {
- todo!()
+impl Serialize for Fields {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> {
+ vec_raw_write(parcel, &self.0)
}
}
-pub struct Infos {
- app_infos: HashMap,
- user_infos: UserInfo,
+impl Deserialize for Fields {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult {
+ let result = Fields(vec_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct OrderTable {
+ pub(crate) alias: String,
+ pub(crate) table_name: String,
+ pub(crate) fields: Fields,
}
-impl Infos {
- pub fn get_app_info(&self) -> &AppInfo {
- todo!()
+impl Serialize for OrderTable {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ parcel.write(&self.alias)?;
+ parcel.write(&self.table_name)?;
+ parcel.write(&self.fields)?;
+ Ok(())
}
+}
- pub fn get_user_info(&self) -> &UserInfo {
- todo!()
+impl Deserialize for OrderTable {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = OrderTable {
+ alias: parcel.read::()?,
+ table_name: parcel.read::()?,
+ fields: parcel.read::()?,
+ };
+ Ok(result)
}
}
-pub struct RecordResponse {
- time: i64,
- device_name: String,
- app_id: String,
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct SchemaOrderTables(pub(crate) Vec);
+
+impl Serialize for SchemaOrderTables {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ vec_raw_write(parcel, &self.0)
+ }
+}
+
+impl Deserialize for SchemaOrderTables {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = SchemaOrderTables(vec_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct Databases(pub(crate) Vec);
+
+impl Serialize for Databases {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ vec_raw_write(parcel, &self.0)
+ }
}
-pub enum OperationType {
- None,
- Add,
- Update,
- Delete,
+impl Deserialize for Databases {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let databases = Databases(vec_raw_read(parcel)?);
+ Ok(databases)
+ }
}
-pub struct Asset {
- uri: String,
- asset_name: String,
- operation_type: OperationType,
- hash: String,
- version: u64,
- asset_id: String,
- sub_path: String,
+#[derive(Default)]
+pub(crate) struct Schema {
+ pub(crate) version: i32,
+ pub(crate) bundle_name: String,
+ pub(crate) databases: Databases,
}
-pub struct Reference {
- record_id: String,
- record_type: String,
+impl Schema {
+ fn read(&mut self, msg_parcel: &mut MsgParcel) -> Result<(), Error> {
+ self.version = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ self.bundle_name = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ self.databases = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ Ok(())
+ }
}
-pub enum RecordField {
- Null(),
- Int(i64),
- Double(f64),
- String(String),
- Bool(bool),
- Blob(Vec),
- List(Vec),
- Map(HashMap),
- Asset(Asset),
- Reference(Reference),
+impl Deserialize for Schema {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let version = parcel.read::()?;
+ let bundle_name = parcel.read::()?;
+ let databases = parcel.read::()?;
+
+ let result = Schema {
+ version,
+ bundle_name,
+ databases,
+ };
+ Ok(result)
+ }
}
-pub struct Record {
- record_id: String,
- record_type: String,
- create_info: RecordResponse,
- modified_info: RecordResponse,
- record_data: HashMap,
- is_delete: bool,
- version: u32,
- is_new_create: bool,
+/************** All AppInfo struct-related structures and methods **************/
+
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct AppInfo {
+ pub(crate) bundle_name: String,
+ pub(crate) id: String,
+ pub(crate) fingerprint: String,
+ pub(crate) enable_cloud: bool,
}
-impl Record {
- pub fn get_record_id(&self) -> &str {
- self.record_id.as_str()
+impl Deserialize for AppInfo {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let bundle_name = parcel.read::()?;
+ let id = parcel.read::()?;
+ let fingerprint = parcel.read::()?;
+ let enable_cloud = parcel.read::()?;
+
+ let result = Self {
+ bundle_name,
+ id,
+ fingerprint,
+ enable_cloud,
+ };
+ Ok(result)
}
+}
+
+/************** All ServiceInfo struct-related structures and methods **************/
+#[derive(Ord, PartialOrd, Eq, PartialEq)]
+pub(crate) enum CloudStatus {
+ Unknown,
+ UnActivated,
+ Login,
+ Logout,
+}
- pub fn get_record_type(&self) -> &str {
- self.record_type.as_str()
+impl CloudStatus {
+ fn from_u8(data: u8) -> Result {
+ match data {
+ 0_u8 => Ok(Self::Unknown),
+ 1_u8 => Ok(Self::UnActivated),
+ 2_u8 => Ok(Self::Login),
+ 3_u8 => Ok(Self::Logout),
+ _ => Err(Error::InvalidCloudStatus),
+ }
}
+}
- pub fn get_create_info(&self) -> &RecordResponse {
- &self.create_info
+impl Default for CloudStatus {
+ fn default() -> Self {
+ Self::Unknown
}
+}
+
+#[derive(Default)]
+pub(crate) struct ServiceInfo {
+ pub(crate) account_id: String,
+ pub(crate) cloud_status: CloudStatus,
+ pub(crate) total_space: u64,
+ pub(crate) left_space: u64,
+}
- pub fn get_modified_info(&self) -> &RecordResponse {
- &self.modified_info
+impl ServiceInfo {
+ fn read(&mut self, msg_parcel: &mut MsgParcel) -> Result<(), Error> {
+ self.account_id = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ self.cloud_status = CloudStatus::from_u8(
+ msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?,
+ )?;
+ self.total_space = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ self.left_space = msg_parcel
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ Ok(())
}
+}
+
+/************** All Subscribe struct-related structures and methods **************/
- pub fn get_record_data(&self) -> &HashMap {
- &self.record_data
+#[derive(Default)]
+pub(crate) struct Subscription;
+
+#[derive(Default, Debug, PartialEq)]
+pub(crate) struct SubscriptionResultValue(pub(crate) Vec<(String, u32)>);
+
+impl Deserialize for SubscriptionResultValue {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let length = parcel.read::()? as usize;
+ let mut vector = Vec::with_capacity(length);
+ for _ in 0..length {
+ let alias = parcel.read::()?;
+ let subscription_id = parcel.read::()?;
+ vector.push((alias, subscription_id));
+ }
+ Ok(SubscriptionResultValue(vector))
}
+}
- pub fn get_is_delete(&self) -> bool {
- self.is_delete
+#[derive(Default)]
+pub(crate) struct SubscriptionResult(pub(crate) (u64, HashMap));
+
+impl Deserialize for SubscriptionResult {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let interval = parcel.read::()?;
+ let result = SubscriptionResult((
+ interval,
+ hash_map_raw_read::(parcel)?,
+ ));
+ Ok(result)
}
+}
- pub fn get_version(&self) -> u32 {
- self.version
+#[derive(Default)]
+pub(crate) struct UnsubscriptionInfo(pub(crate) HashMap>);
+
+impl Serialize for UnsubscriptionInfo {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ hash_map_raw_write(parcel, &self.0)
}
+}
+
+impl Subscription {
+ // For subsequent extensibility, use `&self` here.
+ pub(crate) fn subscribe(
+ &self,
+ remote: &Option,
+ expiration: u64,
+ bundle_name: &str,
+ databases: &Databases,
+ ) -> Result {
+ let mut msg_parcel = MsgParcel::new().ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?;
+ msg_parcel
+ .write(&expiration)
+ .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?;
+ msg_parcel
+ .write(bundle_name)
+ .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?;
+ msg_parcel
+ .write(databases)
+ .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?;
+
+ let function_number = Subscribe as u32;
+ let remote_obj = remote
+ .as_ref()
+ .ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Errors(vec![Error::SendRequestFailed]))?;
- pub fn get_is_new_create(&self) -> bool {
- self.is_new_create
+ if let Ok(cloud_subscription_info) = receive.read::() {
+ return Ok(cloud_subscription_info);
+ }
+
+ if let Ok(errs) = receive.read::() {
+ return Err(errs);
+ }
+
+ Err(Errors::default())
}
- // And more...
+ pub(crate) fn unsubscribe(
+ &self,
+ remote: &Option,
+ info: &UnsubscriptionInfo,
+ ) -> Result<(), Errors> {
+ let mut msg_parcel = MsgParcel::new().ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?;
+ msg_parcel
+ .write(info)
+ .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?;
+
+ let function_number = Unsubscribe as u32;
+ let remote_obj = remote
+ .as_ref()
+ .ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Errors(vec![Error::SendRequestFailed]))?;
+
+ if let Ok(errs) = receive.read::() {
+ return Err(errs);
+ }
+
+ Ok(())
+ }
}
-pub struct Lock {
- interval: i32,
- session_id: String,
+/************** All Connect struct-related structures and methods **************/
+
+pub(crate) struct Connect {
+ pub(crate) remote_obj: Option,
+ infos: HashMap,
}
-pub struct Database {
- // And more...
+impl Drop for Connect {
+ fn drop(&mut self) {
+ println!("进入 Connect Drop 流程了");
+ }
}
-impl Database {
- pub fn generate_ids(number: usize) -> Vec {
- todo!()
+impl Connect {
+ // Creates a structure `Connect` for connecting to the cloud and getting various information about the cloud.
+ pub(crate) fn new(user_id: i32) -> Result {
+ let remote_obj =
+ unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) }
+ .ok_or(Error::GetProxyObjectFailed)?;
+ Ok(Self {
+ remote_obj: Some(remote_obj),
+ infos: HashMap::default(),
+ })
}
- pub fn save_records(&mut self) {
- todo!()
+ // Sends `MsgParcel` to the cloud,
+ // wait for the return value from the cloud,
+ // get the required app infos from the cloud.
+ pub(crate) fn get_app_brief_info(
+ &mut self,
+ user_id: i32,
+ ) -> Result, Error> {
+ self.infos.entry(user_id).or_insert_with(Infos::default);
+ let infos = self.infos.get_mut(&user_id).unwrap();
+
+ let mut lock = infos.app_infos.write().unwrap();
+
+ let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+
+ let function_number = GetAppBriefInfo as u32;
+ let remote_obj = self
+ .remote_obj
+ .as_ref()
+ .ok_or(Error::GetProxyObjectFailed)?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+
+ lock.0 = receive
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?
+ .0;
+
+ drop(lock);
+
+ Ok(infos.app_infos.read().unwrap())
}
- pub fn delete_records(&mut self) { todo!() }
+ // Sends `MsgParcel` to the cloud,
+ // wait for the return value from the cloud,
+ // get the required user info from the cloud.
+ pub(crate) fn get_service_info(
+ &mut self,
+ user_id: i32,
+ ) -> Result, Error> {
+ self.infos.entry(user_id).or_insert_with(Infos::default);
+ let infos = self.infos.get_mut(&user_id).unwrap();
- pub fn fetch_records(&mut self) { todo!() }
+ let mut lock = infos.service_info.write().unwrap();
- pub fn get_start_cursor(&mut self) { todo!() }
+ let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
- pub fn fetch_database_changes(&mut self) { todo!() }
+ let function_number = GetServiceInfo as u32;
+ let remote_obj = self
+ .remote_obj
+ .clone()
+ .ok_or(Error::CreateMsgParcelFailed)?;
+ let mut receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
- pub fn get_lock(&mut self) { todo!() }
+ lock.read(&mut receive)?;
- pub fn delete_lock(&mut self) { todo!() }
+ drop(lock);
+
+ Ok(infos.service_info.read().unwrap())
+ }
- // And more...
+ pub(crate) fn get_app_schema(
+ &mut self,
+ user_id: i32,
+ bundle_name: &str,
+ ) -> Result, Error> {
+ self.infos.entry(user_id).or_insert_with(Infos::default);
+ let infos = self.infos.get_mut(&user_id).unwrap();
+
+ let mut lock = infos.app_schema.write().unwrap();
+
+ let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+ msg_parcel
+ .write(bundle_name)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+
+ let function_number = GetAppSchema as u32;
+ let remote_obj = self
+ .remote_obj
+ .clone()
+ .ok_or(Error::CreateMsgParcelFailed)?;
+ let mut receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+
+ lock.read(&mut receive)?;
+
+ drop(lock);
+
+ Ok(infos.app_schema.read().unwrap())
+ }
+
+ pub(crate) fn subscribe(
+ &self,
+ expiration: u64,
+ bundle_name: &str,
+ databases: &Databases,
+ ) -> Result {
+ let subscription = Subscription::default();
+ subscription.subscribe(&self.remote_obj, expiration, bundle_name, databases)
+ }
+
+ pub(crate) fn unsubscribe(&self, info: &UnsubscriptionInfo) -> Result<(), Errors> {
+ let subscription = Subscription::default();
+ subscription.unsubscribe(&self.remote_obj, info)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct AppInfos(pub(crate) Vec);
+
+impl Deserialize for AppInfos {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = AppInfos(vec_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct Infos {
+ app_infos: RwLock,
+ service_info: RwLock,
+ app_schema: RwLock,
+}
+
+#[cfg(all(test))]
+mod test {
+ use crate::ipc_conn::connect::{FieldType, SubscriptionResultValue};
+ use crate::ipc_conn::{
+ AppInfo, Connect, Database, Databases, OrderTable, Schema, SchemaOrderTables,
+ UnsubscriptionInfo,
+ };
+ use ipc_rust::MsgParcel;
+
+ /// UT test for order_table_serialize.
+ ///
+ /// # Title
+ /// ut_order_table_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `OrderTable` struct.
+ /// 2. Serialize the struct.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_order_table_serialize() {
+ let order_table = OrderTable::default();
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&order_table).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), String::default());
+ assert_eq!(msg_parcel.read::().unwrap(), String::default());
+ }
+
+ /// UT test for order_table_deserialize.
+ ///
+ /// # Title
+ /// ut_order_table_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `OrderTable` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `OrderTable`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_order_table_deserialize() {
+ let order_table = OrderTable::default();
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&order_table).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), order_table);
+ }
+
+ /// UT test for schema_order_tables_serialize.
+ ///
+ /// # Title
+ /// ut_schema_order_tables_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `SchemaOrderTables` struct.
+ /// 2. Serialize the struct.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_schema_order_tables_serialize() {
+ let mut schema_order_tables = SchemaOrderTables::default();
+ let order_table_one = OrderTable::default();
+ let order_table_two = OrderTable::default();
+ schema_order_tables.0.push(order_table_one);
+ schema_order_tables.0.push(order_table_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&schema_order_tables).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), 2);
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ OrderTable::default()
+ );
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ OrderTable::default()
+ );
+ }
+
+ /// UT test for schema_order_tables_deserialize.
+ ///
+ /// # Title
+ /// ut_schema_order_tables_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `SchemaOrderTables` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `SchemaOrderTables`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_schema_order_tables_deserialize() {
+ let mut schema_order_tables = SchemaOrderTables::default();
+ let order_table_one = OrderTable::default();
+ let order_table_two = OrderTable::default();
+ schema_order_tables.0.push(order_table_one);
+ schema_order_tables.0.push(order_table_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&schema_order_tables).unwrap();
+
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ schema_order_tables
+ );
+ }
+
+ /// UT test for databases_serialize.
+ ///
+ /// # Title
+ /// ut_databases_serialize
+ ///
+ /// # Brief
+ /// 1. Create a `Databases` struct.
+ /// 2. Serialize the struct.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_databases_serialize() {
+ let mut databases = Databases::default();
+ let database_one = Database::default();
+ let database_two = Database::default();
+ databases.0.push(database_one);
+ databases.0.push(database_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&databases).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), 2);
+ assert!(msg_parcel.read::().is_ok());
+ assert!(msg_parcel.read::().is_ok());
+ }
+
+ /// UT test for databases_deserialize.
+ ///
+ /// # Title
+ /// ut_databases_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `Databases` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `Databases`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_databases_deserialize() {
+ let mut databases = Databases::default();
+ let database_one = Database::default();
+ let database_two = Database::default();
+ databases.0.push(database_one);
+ databases.0.push(database_two);
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&databases).unwrap();
+
+ assert!(msg_parcel.read::().is_ok());
+ }
+
+ /// UT test for schema_deserialize.
+ ///
+ /// # Title
+ /// ut_schema_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `Schema` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `Schema`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_schema_deserialize() {
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&0_i32).unwrap();
+ msg_parcel.write(&String::from("")).unwrap();
+ msg_parcel.write(&Databases::default()).unwrap();
+
+ assert!(msg_parcel.read::().is_ok());
+ }
+
+ /// UT test for app_info_deserialize.
+ ///
+ /// # Title
+ /// ut_app_info_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `AppInfo` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `AppInfo`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_app_info_deserialize() {
+ let app_info = AppInfo::default();
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&String::default()).unwrap();
+ msg_parcel.write(&String::default()).unwrap();
+ msg_parcel.write(&String::default()).unwrap();
+ msg_parcel.write(&bool::default()).unwrap();
+
+ assert_eq!(msg_parcel.read::().unwrap(), app_info);
+ }
+
+ /// UT test for subscription_result_value_deserialize.
+ ///
+ /// # Title
+ /// ut_subscription_result_value_deserialize
+ ///
+ /// # Brief
+ /// 1. Create a `SubscriptionResultValue` struct.
+ /// 2. Serialize the struct.
+ /// 3. Read the data in `MsgParcel`.
+ /// 4. Deserialize the data to `SubscriptionResultValue`.
+ /// 5. Check if it is correct.
+ #[test]
+ fn ut_subscription_result_value_deserialize() {
+ let mut subscription = SubscriptionResultValue::default();
+ subscription.0.push((String::from("test1"), 2));
+
+ let mut msg_parcel = MsgParcel::new().unwrap();
+ msg_parcel.write(&1_u32).unwrap();
+ msg_parcel.write(&String::from("test1")).unwrap();
+ msg_parcel.write(&2_u32).unwrap();
+
+ assert_eq!(
+ msg_parcel.read::().unwrap(),
+ subscription
+ );
+ }
+
+ /// UT test for connect_new.
+ ///
+ /// # Title
+ /// ut_connect_new
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Check if it is correct.
+ #[test]
+ fn ut_connect_new() {
+ let user_id = 100;
+ let connect = Connect::new(user_id).unwrap();
+ assert!(connect.remote_obj.is_some());
+ }
+
+ /// UT test for connect_get_app_brief_info.
+ ///
+ /// # Title
+ /// ut_connect_get_app_brief_info
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Get app infos.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_connect_get_app_brief_info() {
+ let user_id = 100;
+ let mut connect = Connect::new(user_id).unwrap();
+ let app_infos = &connect.get_app_brief_info(user_id).unwrap().0;
+ assert_eq!(app_infos.len(), 3);
+ assert_eq!(
+ app_infos[0].bundle_name,
+ String::from("com.huawei.hmos.calendardata")
+ );
+ assert_eq!(app_infos[0].id, String::from("10055832"));
+ assert_eq!(
+ app_infos[1].bundle_name,
+ String::from("com.huawei.hmos.notepad")
+ );
+ assert_eq!(app_infos[1].id, String::from("10055832"));
+ assert_eq!(app_infos[2].bundle_name, String::from("com.ohos.contacts"));
+ assert_eq!(app_infos[2].id, String::from("10055832"));
+ }
+
+ /// UT test for connect_get_service_info.
+ ///
+ /// # Title
+ /// ut_connect_get_service_info
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Get service info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_connect_get_service_info() {
+ let user_id = 100;
+ let mut connect = Connect::new(user_id).unwrap();
+ let service_info = &connect.get_service_info(user_id).unwrap();
+ assert_eq!(service_info.account_id, String::from("2850086000356238647"));
+ assert_eq!(service_info.left_space, 536844326007);
+ assert_eq!(service_info.total_space, 536844326007);
+ }
+
+ /// UT test for connect_get_app_schema.
+ ///
+ /// # Title
+ /// ut_connect_get_app_schema
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Get schema info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_connect_get_app_schema() {
+ let user_id = 100;
+ let bundle_name = String::from("com.huawei.hmos.notepad");
+ let mut connect = Connect::new(user_id).unwrap();
+ let app_schema = &connect.get_app_schema(user_id, &bundle_name).unwrap();
+ assert_eq!(
+ app_schema.bundle_name,
+ String::from("com.huawei.hmos.notepad")
+ );
+ assert_eq!(app_schema.version, 100);
+ assert_eq!(app_schema.databases.0.len(), 5);
+ let databases = &app_schema.databases.0;
+
+ assert!(databases[0].remote_obj.is_none());
+ assert_eq!(databases[0].name, String::from("notepad"));
+ assert_eq!(databases[0].alias, String::from("notepad"));
+
+ assert_eq!(databases[0].tables.0.len(), 5);
+ assert_eq!(databases[0].tables.0[0].alias, String::from("notetag"));
+ assert_eq!(
+ databases[0].tables.0[0].table_name,
+ String::from("cloud_folders")
+ );
+ assert_eq!(databases[0].tables.0[0].fields.0.len(), 4);
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[0].alias,
+ String::from("data")
+ );
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[0].col_name,
+ String::from("data")
+ );
+ assert!(databases[0].tables.0[0].fields.0[0].nullable);
+ assert!(!databases[0].tables.0[0].fields.0[0].primary);
+ assert_eq!(databases[0].tables.0[0].fields.0[0].typ, FieldType::Text);
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[1].alias,
+ String::from("recycled")
+ );
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[1].col_name,
+ String::from("recycled")
+ );
+ assert!(databases[0].tables.0[0].fields.0[1].nullable);
+ assert!(!databases[0].tables.0[0].fields.0[1].primary);
+ assert_eq!(databases[0].tables.0[0].fields.0[1].typ, FieldType::Bool);
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[2].alias,
+ String::from("recycledTime")
+ );
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[2].col_name,
+ String::from("recycledTime")
+ );
+ assert!(databases[0].tables.0[0].fields.0[2].nullable);
+ assert!(!databases[0].tables.0[0].fields.0[2].primary);
+ assert_eq!(databases[0].tables.0[0].fields.0[2].typ, FieldType::Number);
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[3].alias,
+ String::from("uuid")
+ );
+ assert_eq!(
+ databases[0].tables.0[0].fields.0[3].col_name,
+ String::from("uuid")
+ );
+ assert!(databases[0].tables.0[0].fields.0[3].nullable);
+ assert!(databases[0].tables.0[0].fields.0[3].primary);
+ assert_eq!(databases[0].tables.0[0].fields.0[3].typ, FieldType::Text);
+
+ assert_eq!(databases[0].tables.0[1].alias, String::from("shorthand"));
+ assert_eq!(
+ databases[0].tables.0[1].table_name,
+ String::from("cloud_tasks")
+ );
+ assert_eq!(databases[0].tables.0[1].fields.0.len(), 5);
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[0].alias,
+ String::from("attachments")
+ );
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[0].col_name,
+ String::from("attachments")
+ );
+ assert!(databases[0].tables.0[1].fields.0[0].nullable);
+ assert!(!databases[0].tables.0[1].fields.0[0].primary);
+ assert_eq!(databases[0].tables.0[1].fields.0[0].typ, FieldType::Assets);
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[1].alias,
+ String::from("data")
+ );
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[1].col_name,
+ String::from("data")
+ );
+ assert!(databases[0].tables.0[1].fields.0[1].nullable);
+ assert!(!databases[0].tables.0[1].fields.0[1].primary);
+ assert_eq!(databases[0].tables.0[1].fields.0[1].typ, FieldType::Text);
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[2].alias,
+ String::from("recycled")
+ );
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[2].col_name,
+ String::from("recycled")
+ );
+ assert!(databases[0].tables.0[1].fields.0[2].nullable);
+ assert!(!databases[0].tables.0[1].fields.0[2].primary);
+ assert_eq!(databases[0].tables.0[1].fields.0[2].typ, FieldType::Bool);
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[3].alias,
+ String::from("recycledTime")
+ );
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[3].col_name,
+ String::from("recycledTime")
+ );
+ assert!(databases[0].tables.0[1].fields.0[3].nullable);
+ assert!(!databases[0].tables.0[1].fields.0[3].primary);
+ assert_eq!(databases[0].tables.0[1].fields.0[3].typ, FieldType::Number);
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[4].alias,
+ String::from("uuid")
+ );
+ assert_eq!(
+ databases[0].tables.0[1].fields.0[4].col_name,
+ String::from("uuid")
+ );
+ assert!(databases[0].tables.0[1].fields.0[4].nullable);
+ assert!(databases[0].tables.0[1].fields.0[4].primary);
+ assert_eq!(databases[0].tables.0[1].fields.0[4].typ, FieldType::Text);
+
+ assert_eq!(databases[0].tables.0[2].alias, String::from("note"));
+ assert_eq!(
+ databases[0].tables.0[2].table_name,
+ String::from("cloud_old_notes")
+ );
+ assert_eq!(databases[0].tables.0[2].fields.0.len(), 5);
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[0].alias,
+ String::from("attachments")
+ );
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[0].col_name,
+ String::from("attachments")
+ );
+ assert!(databases[0].tables.0[2].fields.0[0].nullable);
+ assert!(!databases[0].tables.0[2].fields.0[0].primary);
+ assert_eq!(databases[0].tables.0[2].fields.0[0].typ, FieldType::Assets);
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[1].alias,
+ String::from("data")
+ );
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[1].col_name,
+ String::from("data")
+ );
+ assert!(databases[0].tables.0[2].fields.0[1].nullable);
+ assert!(!databases[0].tables.0[2].fields.0[1].primary);
+ assert_eq!(databases[0].tables.0[2].fields.0[1].typ, FieldType::Text);
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[2].alias,
+ String::from("recycled")
+ );
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[2].col_name,
+ String::from("recycled")
+ );
+ assert!(databases[0].tables.0[2].fields.0[2].nullable);
+ assert!(!databases[0].tables.0[2].fields.0[2].primary);
+ assert_eq!(databases[0].tables.0[2].fields.0[2].typ, FieldType::Bool);
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[3].alias,
+ String::from("recycledTime")
+ );
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[3].col_name,
+ String::from("recycledTime")
+ );
+ assert!(databases[0].tables.0[2].fields.0[3].nullable);
+ assert!(!databases[0].tables.0[2].fields.0[3].primary);
+ assert_eq!(databases[0].tables.0[2].fields.0[3].typ, FieldType::Number);
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[4].alias,
+ String::from("uuid")
+ );
+ assert_eq!(
+ databases[0].tables.0[2].fields.0[4].col_name,
+ String::from("uuid")
+ );
+ assert!(databases[0].tables.0[2].fields.0[4].nullable);
+ assert!(databases[0].tables.0[2].fields.0[4].primary);
+ assert_eq!(databases[0].tables.0[2].fields.0[4].typ, FieldType::Text);
+
+ assert_eq!(
+ databases[0].tables.0[3].alias,
+ String::from("noteattachment")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].table_name,
+ String::from("cloud_attachments")
+ );
+ assert_eq!(databases[0].tables.0[3].fields.0.len(), 5);
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[0].alias,
+ String::from("attachments")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[0].col_name,
+ String::from("attachments")
+ );
+ assert!(databases[0].tables.0[3].fields.0[0].nullable);
+ assert!(!databases[0].tables.0[3].fields.0[0].primary);
+ assert_eq!(databases[0].tables.0[3].fields.0[0].typ, FieldType::Assets);
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[1].alias,
+ String::from("data")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[1].col_name,
+ String::from("data")
+ );
+ assert!(databases[0].tables.0[3].fields.0[1].nullable);
+ assert!(!databases[0].tables.0[3].fields.0[1].primary);
+ assert_eq!(databases[0].tables.0[3].fields.0[1].typ, FieldType::Text);
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[2].alias,
+ String::from("recycled")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[2].col_name,
+ String::from("recycled")
+ );
+ assert!(databases[0].tables.0[3].fields.0[2].nullable);
+ assert!(!databases[0].tables.0[3].fields.0[2].primary);
+ assert_eq!(databases[0].tables.0[3].fields.0[2].typ, FieldType::Bool);
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[3].alias,
+ String::from("recycledTime")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[3].col_name,
+ String::from("recycledTime")
+ );
+ assert!(databases[0].tables.0[3].fields.0[3].nullable);
+ assert!(!databases[0].tables.0[3].fields.0[3].primary);
+ assert_eq!(databases[0].tables.0[3].fields.0[3].typ, FieldType::Number);
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[4].alias,
+ String::from("uuid")
+ );
+ assert_eq!(
+ databases[0].tables.0[3].fields.0[4].col_name,
+ String::from("uuid")
+ );
+ assert!(databases[0].tables.0[3].fields.0[4].nullable);
+ assert!(databases[0].tables.0[3].fields.0[4].primary);
+ assert_eq!(databases[0].tables.0[3].fields.0[4].typ, FieldType::Text);
+
+ assert_eq!(databases[0].tables.0[4].alias, String::from("newnote"));
+ assert_eq!(
+ databases[0].tables.0[4].table_name,
+ String::from("cloud_notes")
+ );
+ assert_eq!(databases[0].tables.0[4].fields.0.len(), 5);
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[0].alias,
+ String::from("attachments")
+ );
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[0].col_name,
+ String::from("attachments")
+ );
+ assert!(databases[0].tables.0[4].fields.0[0].nullable);
+ assert!(!databases[0].tables.0[4].fields.0[0].primary);
+ assert_eq!(databases[0].tables.0[4].fields.0[0].typ, FieldType::Assets);
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[1].alias,
+ String::from("data")
+ );
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[1].col_name,
+ String::from("data")
+ );
+ assert!(databases[0].tables.0[4].fields.0[1].nullable);
+ assert!(!databases[0].tables.0[4].fields.0[1].primary);
+ assert_eq!(databases[0].tables.0[4].fields.0[1].typ, FieldType::Text);
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[2].alias,
+ String::from("recycled")
+ );
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[2].col_name,
+ String::from("recycled")
+ );
+ assert!(databases[0].tables.0[4].fields.0[2].nullable);
+ assert!(!databases[0].tables.0[4].fields.0[2].primary);
+ assert_eq!(databases[0].tables.0[4].fields.0[2].typ, FieldType::Bool);
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[3].alias,
+ String::from("recycledTime")
+ );
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[3].col_name,
+ String::from("recycledTime")
+ );
+ assert!(databases[0].tables.0[4].fields.0[3].nullable);
+ assert!(!databases[0].tables.0[4].fields.0[3].primary);
+ assert_eq!(databases[0].tables.0[4].fields.0[3].typ, FieldType::Number);
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[4].alias,
+ String::from("uuid")
+ );
+ assert_eq!(
+ databases[0].tables.0[4].fields.0[4].col_name,
+ String::from("uuid")
+ );
+ assert!(databases[0].tables.0[4].fields.0[4].nullable);
+ assert!(databases[0].tables.0[4].fields.0[4].primary);
+ assert_eq!(databases[0].tables.0[4].fields.0[4].typ, FieldType::Text);
+ }
+
+ /// UT test for connect_subscribe.
+ ///
+ /// # Title
+ /// ut_connect_subscribe
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Get subscribe info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_connect_subscribe() {
+ let user_id = 100;
+ let expiration = 1693488896462;
+ let bundle_name = String::from("com.huawei.hmos.notepad");
+ let mut connect = Connect::new(user_id).unwrap();
+ let app_schema = connect.get_app_schema(user_id, &bundle_name).unwrap();
+ let databases = &app_schema.databases;
+ let result = connect.subscribe(expiration, &bundle_name, databases);
+ assert!(result.is_ok());
+ }
+
+ /// UT test for connect_unsubscribe.
+ ///
+ /// # Title
+ /// ut_connect_unsubscribe
+ ///
+ /// # Brief
+ /// 1. Create a `Connect` struct.
+ /// 2. Get unsubscribe info.
+ /// 3. Check if it is correct.
+ #[test]
+ fn ut_connect_unsubscribe() {
+ let user_id = 100;
+ let expiration = 1693488896462;
+ let bundle_name = String::from("com.huawei.hmos.notepad");
+ let mut connect = Connect::new(user_id).unwrap();
+ let app_schema = connect.get_app_schema(user_id, &bundle_name).unwrap();
+ let databases = &app_schema.databases;
+ let result = connect
+ .subscribe(expiration, &bundle_name, databases)
+ .unwrap();
+
+ let mut unsubscribe_info = UnsubscriptionInfo::default();
+ for info in result.0 .1 {
+ let mut temp = vec![];
+ for (_, id) in &info.1 .0 {
+ temp.push(id.clone());
+ }
+ unsubscribe_info.0.insert(info.0, temp);
+ }
+ let result = connect.unsubscribe(&unsubscribe_info);
+ assert!(result.is_ok());
+ }
}
diff --git a/src/cloud_extension/src/ipc_conn/database.rs b/src/cloud_extension/src/ipc_conn/database.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dd37a85f126eb284e9630f1370eefb8785318b72
--- /dev/null
+++ b/src/cloud_extension/src/ipc_conn/database.rs
@@ -0,0 +1,954 @@
+// Copyright (c) 2023 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::ipc_conn::asset::{Asset, Assets};
+use crate::ipc_conn::connect::SchemaOrderTables;
+use crate::ipc_conn::error::Error;
+use crate::ipc_conn::ffi::ConnectService;
+use crate::ipc_conn::function::CloudDB::{
+ Delete, GenerateIds, Heartbeat, Insert, Lock, Query, Unlock, Update,
+};
+use crate::ipc_conn::function::CloudServiceFunc::ConnectDatabase;
+use crate::ipc_conn::{hash_map_raw_read, hash_map_raw_write, vec_raw_read, vec_raw_write};
+use ipc_rust::{
+ BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcStatusCode, MsgParcel, RemoteObj,
+ Serialize,
+};
+use std::collections::HashMap;
+
+#[derive(Default, Debug, PartialEq, Clone)]
+pub(crate) struct Response {
+ pub(crate) time: i64,
+ pub(crate) device_name: String,
+ pub(crate) app_id: String,
+}
+
+impl Serialize for Response {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ parcel.write(&self.time)?;
+ parcel.write(&self.device_name)?;
+ parcel.write(&self.app_id)?;
+ Ok(())
+ }
+}
+
+impl Deserialize for Response {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let time = parcel.read::()?;
+ let device_name = parcel.read::()?;
+ let app_id = parcel.read::()?;
+
+ let result = Response {
+ time,
+ device_name,
+ app_id,
+ };
+ Ok(result)
+ }
+}
+
+pub(crate) enum Field {
+ Null,
+ Number(i64),
+ Real(f64),
+ Text(String),
+ Bool(bool),
+ Blob(Vec),
+ Asset(Asset),
+ Assets(Assets),
+}
+
+impl From for u8 {
+ fn from(value: Field) -> Self {
+ match value {
+ Field::Null => 0,
+ Field::Number(_) => 1,
+ Field::Real(_) => 2,
+ Field::Text(_) => 3,
+ Field::Bool(_) => 4,
+ Field::Blob(_) => 5,
+ Field::Asset(_) => 6,
+ Field::Assets(_) => 7,
+ }
+ }
+}
+
+impl Serialize for Field {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ match self {
+ Field::Null => parcel.write(&0_u32),
+ Field::Number(number) => {
+ parcel.write(&1_u32)?;
+ parcel.write(number)
+ }
+ Field::Real(real) => {
+ parcel.write(&2_u32)?;
+ parcel.write(real)
+ }
+ Field::Text(text) => {
+ parcel.write(&3_u32)?;
+ parcel.write(text)
+ }
+ Field::Bool(boolean) => {
+ parcel.write(&4_u32)?;
+ parcel.write(boolean)
+ }
+ Field::Blob(blob) => {
+ parcel.write(&5_u32)?;
+ parcel.write(blob)
+ }
+ Field::Asset(asset) => {
+ parcel.write(&6_u32)?;
+ parcel.write(asset)
+ }
+ Field::Assets(assets) => {
+ parcel.write(&7_u32)?;
+ parcel.write(assets)
+ }
+ }
+ }
+}
+
+impl Deserialize for Field {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let index = parcel.read::()?;
+ match index {
+ 0 => Ok(Field::Null),
+ 1 => {
+ let number = parcel.read::()?;
+ Ok(Field::Number(number))
+ }
+ 2 => {
+ let real = parcel.read::()?;
+ Ok(Field::Real(real))
+ }
+ 3 => {
+ let text = parcel.read::()?;
+ Ok(Field::Text(text))
+ }
+ 4 => {
+ let boolean = parcel.read::()?;
+ Ok(Field::Bool(boolean))
+ }
+ 5 => {
+ let blob = parcel.read::>()?;
+ Ok(Field::Blob(blob))
+ }
+ 6 => {
+ let asset = parcel.read::()?;
+ Ok(Field::Asset(asset))
+ }
+ 7 => {
+ let assets = parcel.read::()?;
+ Ok(Field::Assets(assets))
+ }
+ _ => Err(IpcStatusCode::Failed),
+ }
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct ValueBucketData(pub(crate) HashMap);
+
+impl Serialize for ValueBucketData {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ hash_map_raw_write(parcel, &self.0)
+ }
+}
+
+impl Deserialize for ValueBucketData {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = ValueBucketData(hash_map_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct ValueBucket {
+ pub(crate) id: String,
+ pub(crate) typ: String,
+ pub(crate) create_info: Response,
+ pub(crate) modified_info: Response,
+ pub(crate) data: ValueBucketData,
+ pub(crate) is_delete: bool,
+ pub(crate) version: u32,
+ pub(crate) is_new_create: bool,
+}
+
+impl Serialize for ValueBucket {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ parcel.write(&self.id)?;
+ parcel.write(&self.typ)?;
+ parcel.write(&self.create_info)?;
+ parcel.write(&self.modified_info)?;
+ parcel.write(&self.data)?;
+ parcel.write(&self.is_delete)?;
+ parcel.write(&self.version)?;
+ parcel.write(&self.is_new_create)?;
+ Ok(())
+ }
+}
+
+impl Deserialize for ValueBucket {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let id = parcel.read::()?;
+ let typ = parcel.read::()?;
+ let create_info = parcel.read::()?;
+ let modified_info = parcel.read::()?;
+ let data = parcel.read::()?;
+ let is_delete = parcel.read::()?;
+ let version = parcel.read::()?;
+ let is_new_create = parcel.read::()?;
+
+ let result = ValueBucket {
+ id,
+ typ,
+ create_info,
+ modified_info,
+ data,
+ is_delete,
+ version,
+ is_new_create,
+ };
+ Ok(result)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct ValueBuckets(pub(crate) Vec);
+
+impl Serialize for ValueBuckets {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ vec_raw_write(parcel, &self.0)
+ }
+}
+
+impl Deserialize for ValueBuckets {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let result = ValueBuckets(vec_raw_read::(parcel)?);
+ Ok(result)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct CloudData {
+ pub(crate) next_cursor: String,
+ pub(crate) has_more: bool,
+ pub(crate) values: ValueBuckets,
+}
+
+impl Deserialize for CloudData {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let next_cursor = parcel.read::()?;
+ let has_more = parcel.read::()?;
+ let values = parcel.read::()?;
+
+ let cloud_data = CloudData {
+ next_cursor,
+ has_more,
+ values,
+ };
+ Ok(cloud_data)
+ }
+}
+
+#[derive(Default)]
+pub(crate) struct Database {
+ pub(crate) remote_obj: Option,
+ pub(crate) name: String,
+ pub(crate) alias: String,
+ pub(crate) tables: SchemaOrderTables,
+ pub(crate) lock_session_id: String,
+}
+
+impl Serialize for Database {
+ fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> {
+ parcel.write(&self.name)?;
+ parcel.write(&self.alias)?;
+ parcel.write(&self.tables)?;
+ parcel.write(&self.lock_session_id)?;
+ Ok(())
+ }
+}
+
+impl Deserialize for Database {
+ fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult {
+ let name = parcel.read::()?;
+ let cloud_name = parcel.read::()?;
+ let tables = parcel.read::()?;
+
+ let result = Database {
+ remote_obj: None,
+ name,
+ alias: cloud_name,
+ tables,
+ lock_session_id: "".to_string(),
+ };
+ Ok(result)
+ }
+}
+
+impl Database {
+ pub(crate) fn new(user_id: i32, bundle_name: &str) -> Result {
+ let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?;
+ msg_parcel
+ .write(&bundle_name)
+ .map_err(|_| Error::WriteMsgParcelFailed)?;
+
+ let function_number = ConnectDatabase as u32;
+ let remote_obj =
+ unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) }
+ .ok_or(Error::GetProxyObjectFailed)?;
+ let receive = remote_obj
+ .send_request(function_number, &msg_parcel, false)
+ .map_err(|_| Error::SendRequestFailed)?;
+ let mut result = receive
+ .read::()
+ .map_err(|_| Error::ReadMsgParcelFailed)?;
+ result.remote_obj = Some(remote_obj);
+ Ok(result)
+ }
+
+ pub(crate) fn generate_ids(&mut self, number: u32) -> Result, Error> {
+ #[derive(Default)]
+ struct Ids(Vec