diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba..4a9908a74a2cc7b06901258fdb6908051ac90ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +.idea +*.lock diff --git a/src/cloud_extension/BUILD.gn b/src/cloud_extension/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..014e965a50694feb56828db5d3a00e0879309c8f --- /dev/null +++ b/src/cloud_extension/BUILD.gn @@ -0,0 +1,42 @@ +# 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" + ] + subsystem_name = "distributeddatamgr" + + part_name = "datamgr_service" +} + +ohos_rust_unittest("rust_ylong_cloud_ext_unit_test") { + module_out_path = "//foundation/distributeddatamgr/datamgr_service/services/rust/ylong_cloud_extension/target" + sources = [ "src/lib.rs" ] + + external_deps = [ + "hilog:hilog_rust", + "ipc:ipc_rust" + ] + deps = [ + ":ylong_cloud_extension" + ] +} \ No newline at end of file diff --git a/src/cloud_extension/Cargo.toml b/src/cloud_extension/Cargo.toml index cd717be66c2826bdd3f10df30088cf0c47ec766f..39635d0e7cec6297c15faf04c3ae8b9eafa955cb 100644 --- a/src/cloud_extension/Cargo.toml +++ b/src/cloud_extension/Cargo.toml @@ -1,10 +1,10 @@ [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 +13,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..a79dbf3bb961506a6519ce6ef383e74cb346bf9d --- /dev/null +++ b/src/cloud_extension/include/basic_rust_types.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H +#define CLOUD_EXTENSION_BASIC_RUST_TYPES_H + +#ifndef CLOUD_EXTENSION_FETCH_INFOS_H +#include "fetch_infos.h" +#endif + +#ifndef CLOUD_EXTENSION_ERROR_H +#include "error.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +// String +typedef void String; + +int StringGetContent(String *str, char **content, size_t *len); + +/** +* @brief Destroy a Rust String. +* @param string [IN] +* strLen [IN] +*/ +void StringFree(char *string); + +typedef enum ValueTyp { + Value, + String, + Vec, + Relation, + HashMapValueBucket, + ValueBucket, + Database, + BiTuple, + U32, + Table, + Field, + AppInfo +}; + +// Tuple +typedef void BiTuple; + +BiTuple *BiTupleNewU32(unsigned int a, unsigned int b); + +void BiTupleFree(BiTuple *src); + +// Vector +typedef void Vector; + +Vector *VectorNew(ValueTyp typ); + +ValueTyp VectorGetValueTyp(Vector *src); + +int VectorPush( + Vector *src, + void *value, + size_t valueLen, +); + +int VectorGet( + Vector *src, + size_t idx, + void **value, + size_t *valueLen, +); + +int VectorGetLength(Vector *src, size_t *len); + +void VectorFree(Vector *src); + +// HashMap +typedef void HashMap; + +HashMap *HashMapNew(ValueTyp keyTyp, ValueTyp valueTyp); + +ValueTyp HashMapGetKeyTyp(HashMap *src); + +ValueTyp HashMapGetValueTyp(HashMap *src); + +int HashMapGetLength(HashMap *src, size_t *len); + +int HashMapInsert( + HashMap *src, + void *key, + size_t keyLen, + void *value, + size_t valueLen, +); + +/** + * @brief Iteratively return keypair inside HashMap. Keypair will sequentially output through + * parameters keyVal, keyValLen, value, and valueLen. If all data has been output, null will be set to + * pointers, and 0 will be set to lens. + * @param keys [IN] + * key [OUT] + * keyLen [OUT] + * value [OUT] + * valueLen [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention If users want to travers again, the state can be reset by SchemaKeysIterReset. + */ +int HashMapIterGetKeyPair( + HashMap *src, + void **key, + size_t *keyLen, + void **value, + size_t *valueLen +); + +/** + * @brief Reset HashMap iterator state so that users can iter again. + * @param src [IN] + * @retval SUCCESS + * ERRNO_NULLPTR + */ +void HashMapIterReset(HashMap *src); + +int HashMapGet( + HashMap *src, + void *key, + size_t keyLen, + void **value, + size_t *valueLen, +); + +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..5305ef6308b464e73ff2cb4a08002ef0fd0ee43d --- /dev/null +++ b/src/cloud_extension/include/cloud_ext_types.h @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#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 This type is declared to construct arguments. The memory is managed by Rust, and when passed through + * functions, its memory management will return back to the Rust side, and no memory leak will be caused. + * However, if users create instances of this type but not use them, there will be memory leaks. + */ +typedef void Value; + +/** + * @brief Create an Value instance. + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewEmpty(); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewInt(int src); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewDouble(float src); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewString(const char *src, size_t len); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewBool(bool src); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewBytes(const unsigned int *src, size_t len); + +/** + * @brief Create an Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + * @attention When passed in, Value will take control of the memory management of the Asset. No more free is needed. + */ +Value *ValueNewAsset(const Asset *src); + +/** + * @brief Create a Value instance. + * @param src [IN] + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNewAssets(const Asset *src, size_t len); + +/** + * @brief Type declaration of Rust cloud extension struct Database. Can be obtained from Schema. + */ +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. + */ +Database *DatabaseNew( + char *name, + size_t nameLen, + 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, 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, 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 Type declaration of Rust cloud extension struct Table. Can be obtained from Database. + */ +typedef void Table; + +/** + * @brief Create a Table instance. + * @retval != NULL, a valid pointer of Table. + * @attention When passed in, table will take control of the memory management of the vector field. No more free is needed. + */ +Table *TableNew( + char *name, + size_t nameLen, + char *alias, + size_t aliasLen, + Vector *fields +) + +/** + * @brief Get name from a Table instance. + * @param tb [IN] + * name [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Name returned shouldn't be freed, or issues of double free is possible. + */ +int TableGetName(Table *tb, char **name, size_t *len); + +/** + * @brief Get alias from a Table instance. + * @param tb [IN] + * alias [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Alias returned shouldn't be freed, or issues of double free is possible. + */ +int TableGetAlias(Table *tb, char **alias, size_t *len); + +/** + * @brief Get fields from a Database instance. + * @param tb [IN] + * fields [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention The HashMap returned should be freed by VectorFree. + */ +int TableGetFields(Table *tb, Vector **fields); + +/** + * @brief Type declaration of Rust cloud extension struct Field. Can be obtained from Table. + */ +typedef void Field; + +Field *FieldNew( + char *colName, + size_t colNameLen, + char *alias, + size_t aliasLen, + unsigned char typ, + bool primary, + bool nullable +); + +/** + * @brief Get name from a Field instance. + * @param fd [IN] + * name [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Name returned shouldn't be freed, or issues of double free is possible. + */ +int FieldGetColName(Field *fd, char **name, size_t *len); + +/** + * @brief Get alias from a Field instance. + * @param fd [IN] + * alias [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Alias returned shouldn't be freed, or issues of double free is possible. + */ +int FieldGetAlias(Field *fd, char **alias, size_t *len); + +/** + * @brief Get type of a Field instance. + * @param fd [IN] + * typ [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + */ +int FieldGetTyp(Field *fd, int *typ); + +/** + * @brief Get primacy of a Field instance. + * @param fd [IN] + * primary [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + */ +int FieldGetPrimary(Field *fd, bool *primary); + +/** + * @brief Get nullability of a Field instance. + * @param fd [IN] + * nullable [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + */ +int FieldGetNullable(Field *fd, bool *nullable); + +/** + * @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; + +int CloudInfoGetUser(CloudInfo *info, int *user); + +int CloudInfoGetId( + CloudInfo *info, + char **id, + size_t *idLen, +); + +int CloudInfoGetTotalSpace( + CloudInfo *info, + int *totalSpace +); + +int CloudInfoGetRemainSpace( + CloudInfo *info, + int *remainSpace +); + +int CloudInfoGetEnableCloud( + CloudInfo *info, + bool *enableCloud +); + +/** + * @brief Destroy an CloudInfo instance. + * @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); + +typedef void AppInfo; + +int AppInfoGetAppId( + AppInfo *appInfo, + char **appId, + size_t *idLen, +); + +int AppInfoGetBundleName( + AppInfo *appInfo, + char **bundleName, + size_t *len, +); + +int AppInfoGetCloudSwitch( + AppInfo *appInfo, + bool *cloudSwitch +); + +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; + +int SchemaMetaGetVersion(SchemaMeta *schema, int *version); + +int SchemaMetaGetBundleName(SchemaMeta *schema, char **name, size_t *len); + +/** + * @brief Get databases from a SchemaMeta instance. + * @param schema [IN] + * db [IN] Vec + * @retval SUCCESS + * @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); + +typedef void Relation; + +/** + * @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. + */ +Relation *RelationNew( + const char *bundleName, + size_t nameLen, + HashMap *relations +) + +int RelationGetBundleName(Relation *re, char **name, size_t *len); + +/** + * @attention The hashmap returned should be freed by HashMapFree. + */ +int RelationGetRelations(Relation *re, HashMap **relations); + +void RelationFree(Relation *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; + +int CloudDataGetNextCursor(CloudData *data, char **cursor, size_t *len); + +int CloudDataGetHasMore(CloudData *data, bool *hasMore); + +/** + * @attention The vector returned should be freed by VectorFree. + */ +int CloudDataGetValues(CloudData *data, Vector **values); + +void CloudDataFree(CloudData *ptr); + +typedef void ValueBucket; + +int ValueBucketGetId(ValueBucket *vb, char **id, size_t *len); + +int ValueBucketGetTyp(ValueBucket *vb, char **typ, size_t *len); + +int ValueBucketGetCreateInfo(ValueBucket *vb, VBInfo **info); + +int ValueBucketGetModifiedInfo(ValueBucket *vb, VBInfo **info); + +int ValueBucketGetData(ValueBucket *vb, HashMap **data); + +int ValueBucketIsDelete(CloudData *data, bool *isDelete); + +int ValueBucketGetVersion(CloudData *data, unsigned int *version); + +int ValueBucketIsNewCreate(CloudData *data, bool *isNewCreate); + +typedef void VBInfo; + +int VBInfoGetTime(VBInfo *info, int *time); + +int VBInfoGetDeviceName(VBInfo *info, char **name, size_t *len); + +int VBInfoGetAppId(VBInfo *info, 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..923523fc1fa4ed057d5a81dab6c9518efb0589de 100644 --- a/src/cloud_extension/include/cloud_extension.h +++ b/src/cloud_extension/include/cloud_extension.h @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @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. + */ +typedef void AssetLoader; + +/** + * @brief Create an AssetLoader instance. + * @param userId [IN] + * bundleName [IN] + * nameLen [IN] + * db [IN] Obtained from + * @retval != NULL, a valid pointer of AssetLoader. + */ +AssetLoader *AssetLoaderNew(int userId, const 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] + * @retval SUCCESS + * ERRNO_AssetUploadFailure + */ +int AssetLoaderUpload( + AssetLoader *loader, + const char *tableName, + size_t tableNameLen, + const char *gid, + size_t gidLen, + const char *prefix, + size_t prefixLen, + HashMap *assets, +); + +/** + * @brief Download assets. + * @param loader [IN] + * tableName [IN] + * tableNameLen [IN] + * gid [IN] + * gidLen [IN] + * prefix [IN] + * prefixLen [IN] + * assets [IN/OUT] + * @retval SUCCESS + * ERRNO_AssetDownloadFailure + */ +int AssetLoaderDownload( + AssetLoader *loader, + const char *tableName, + size_t tableNameLen, + const char *gid, + size_t gidLen, + const char *prefix, + size_t prefixLen, + HashMap *assets, +); + +/** + * @brief Remove one asset from the local path. + * @param asset [IN] + * @retval SUCCESS + * ERRNO_IOError + */ +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. + * @param userId [IN] + * database [IN] + * @retval != NULL, a valid pointer of CloudDatabase. + */ +CloudDatabase *CloudDbNew( + const char* bundleName, + size_t nameLen, + Database *database +); + +/** + * @brief Execute a sql on a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * sql [IN] + * sqlLen [IN] + * extend [IN/OUT] + * @retval SUCCESS + * ERRNO_Unsupported + */ +int CloudDbExecuteSql( + CloudDatabase *cdb, + const char* table, + size_t tableLen, + const char* sql, + size_t sqlLen, + HashMap *extend +); + +/** + * @brief Insert batches of value buckets into a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * value [IN] + * extend [IN/OUT] + * @retval SUCCESS + */ +int CloudDbBatchInsert( + CloudDatabase *cdb, + const 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] + * extend [IN/OUT] + * @retval SUCCESS + */ +int CloudDbBatchUpdate( + CloudDatabase *cdb, + const 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] + * @retval SUCCESS + */ +int CloudDbBatchDelete( + CloudDatabase *cdb, + const char* table, + size_t tableLen, + Vector *extend +); + +/** + * @brief Search batches of value buckets from a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * extend [IN/OUT] + * cursor [OUT] The address of a pointer to a Cursor. The pointer value will be updated when + * query function successfully returns. + * @retval SUCCESS + */ +int CloudDbBatchQuery( + CloudDatabase *cdb, + const char* table, + size_t tableLen, + const char* cursor, + size_t cursorLen, + CloudData **out +); + +/** + * @brief Lock a CloudDatabase instance. + * @param cdb [IN] + * @retval SUCCESS + */ +int CloudDbLock(CloudDatabase *cdb, int *expire); + +/** + * @brief Unlock a CloudDatabase instance. + * @param cdb [IN] + * @retval SUCCESS + */ +int CloudDbUnlock(CloudDatabase *cdb); + +/** + * @brief Get heartbeat of a CloudDatabase instance. + * @param cdb [IN] + * @retval SUCCESS + */ +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 CloudInfo from an CloudServer instance. + * @param server [IN] + * info [OUT] + * @retval SUCCESS + */ +int CloudServerGetServiceInfo(CloudServer *server, CloudInfo **info); + +/** + * @brief Create an CloudServer instance. + * @param server [IN] + * bundleName [IN] + * bundleNameLen [IN] + * schema [OUT] + * @retval SUCCESS + * + */ +int CloudServerGetAppSchema(CloudServer *server, const char *bundleName, size_t bundleNameLen, SchemaMeta **schema); + +/** + * @brief Create an CloudServer instance. + * @param server [IN] + * relations [OUT] HashMap, bundle name as key + */ +int CloudServerSubscribe( + CloudServer *server, + const HashMap *dbs, + HashMap **relations, + int **err, + size_t *errLen +); + +/** + * @brief Create an CloudServer instance. + * @param server [IN] + * relations [IN] HashMap>, bundle name as key, ids as value + */ +int CloudServerUnsubscribe( + CloudServer *server, + const HashMap *relations, + int **err, + size_t *errLen +); + +/** + * @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..2505bb2aab4e063af323a7bfe0ddf3942bca71e5 --- /dev/null +++ b/src/cloud_extension/include/error.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#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_NotAnAsset 3 +#define ERRNO_AssetDownloadFailure 4 +#define ERRNO_AssetUploadFailure 5 +#define ERRNO_NoSuchTableInDb 6 +#define ERRNO_Unsupported 7 +#define ERRNO_IPCError 8 +#define ERRNO_IOError 9 +#define ERRNO_NETWORK_ERROR 10 +#define ERRNO_LOCKED_BY_OTHERS 11 +#define ERRNO_RECORD_LIMIT_EXCEEDED 12 +#define ERRNO_NO_SPACE_FOR_ASSET 13 + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //CLOUD_EXTENSION_ERROR_H diff --git a/src/cloud_extension/include/fetch_infos.h b/src/cloud_extension/include/fetch_infos.h new file mode 100644 index 0000000000000000000000000000000000000000..5a81163644a5647ef60bc8237dfa691332c5790b --- /dev/null +++ b/src/cloud_extension/include/fetch_infos.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CLOUD_EXTENSION_FETCH_INFOS_H +#define CLOUD_EXTENSION_FETCH_INFOS_H + +#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H +#include "basic_rust_types.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum AssetStatus { + Unknown, + Normal, + Insert, + Update, + Delete, + Abnormal, + Downloading, + Butt, +}; + +/** +* @brief Type declaration of Rust cloud extension struct Asset. +* @attention This type is declared to construct arguments. The memory is managed by Rust, and when passed through +* functions, its memory management will return back to the Rust side, and no memory leak will be caused. +* However, if users create instances of this type but not use them, there will be memory leaks. +*/ +typedef struct { + unsigned int version; + AssetStatus status; + unsigned int expiresTime; + char *id; + size_t idLen; + char *name; + size_t nameLen; + char *uri; + size_t uriLen; + char *localPath; + size_t localPathLen; + char *createTime; + size_t createTimeLen; + char *modifyTime; + size_t modifyTimeLen; + char *size; + size_t sizeLen; + char *hash; + size_t hashLen; +} Asset; + + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //CLOUD_EXTENSION_FETCH_INFOS_H 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..564f9fe2e0ba2902ecb57c048f330a4f21234ccb --- /dev/null +++ b/src/cloud_extension/src/c_adapter/cloud_ext_types.rs @@ -0,0 +1,900 @@ +use crate::c_adapter::basic_rust_types; +use crate::c_adapter::basic_rust_types::{ + CffiHashMap, CffiVector, ValueType, VectorGetValueTyp, VectorNew, VectorPush, +}; +use crate::c_adapter::{ERRNO_NULLPTR, SUCCESS}; +use crate::service_impl::{asset_loader, cloud_db, cloud_service, types}; +use std::collections::HashMap; +use std::ffi::{c_char, c_int, c_uchar, c_uint, c_void}; +use std::hash::Hash; +use std::ops::Sub; +use std::ptr::{null, null_mut, slice_from_raw_parts}; + +/// Asset defined in C struct. It will convert to Rust Asset struct in use. +#[repr(C)] +pub struct Asset { + pub version: u64, + pub status: asset_loader::AssetStatus, + pub expires_time: u64, + pub id: *const c_char, + pub id_len: usize, + pub name: *const c_char, + pub name_len: usize, + pub uri: *const c_char, + pub uri_len: usize, + pub local_path: *const c_char, + pub local_path_len: usize, + pub create_time: *const c_char, + pub create_time_len: usize, + pub modify_time: *const c_char, + pub modify_time_len: usize, + pub size: *const c_char, + pub size_len: usize, + pub hash: *const c_char, + pub hash_len: usize, +} + +/// Value pointer passed to C side. +pub type Value = 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; +/// Relation pointer passed to C side. +pub type Relation = 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::Empty instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewEmpty() -> *mut Value { + Box::into_raw(Box::new(types::Value::Empty)) as *mut Value +} + +/// Create a Value::Int instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewInt(src: i64) -> *mut Value { + Box::into_raw(Box::new(types::Value::Int(src))) as *mut Value +} + +/// Create a Value::Double instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewDouble(src: f64) -> *mut Value { + Box::into_raw(Box::new(types::Value::Double(src))) as *mut Value +} + +/// Create a Value::String instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewString(src: *const c_char, len: usize) -> *mut Value { + if src.is_null() { + return null_mut(); + } + + let str = char_ptr_to_string(src, len); + Box::into_raw(Box::new(types::Value::String(str))) as *mut Value +} + +/// Create a Value::Bool instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewBool(src: bool) -> *mut Value { + Box::into_raw(Box::new(types::Value::Bool(src))) as *mut Value +} + +/// Create a Value::Bytes instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewBytes(src: *const c_uint, len: usize) -> *mut Value { + if src.is_null() { + return null_mut(); + } + + let slice = &(*slice_from_raw_parts(src as *const u8, len)); + Box::into_raw(Box::new(types::Value::Bytes(slice.to_vec()))) as *mut Value +} + +/// Create a Value::Asset instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewAsset(src: *const Asset) -> *mut Value { + if src.is_null() { + return null_mut(); + } + + let asset = get_asset(src); + Box::into_raw(Box::new(types::Value::Asset(asset))) as *mut Value +} + +/// Create a Value::Assets instance. +#[no_mangle] +pub unsafe extern "C" fn ValueNewAssets(src: *const Asset, len: usize) -> *mut Value { + if src.is_null() { + return null_mut(); + } + + let asset_slice = &*slice_from_raw_parts(src, len); + let mut assets = vec![]; + for asset_cffi in asset_slice { + assets.push(get_asset(asset_cffi)); + } + Box::into_raw(Box::new(types::Value::Assets(assets))) as *mut Value +} + +unsafe fn char_ptr_to_string(ptr: *const c_char, 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()) +} + +unsafe fn get_asset(src: *const Asset) -> asset_loader::Asset { + let mut asset = asset_loader::Asset::default(); + let src_asset = &*src; + let id = char_ptr_to_string(src_asset.id, src_asset.id_len); + asset.set_id(id); + let name = char_ptr_to_string(src_asset.name, src_asset.name_len); + asset.set_name(name); + let uri = char_ptr_to_string(src_asset.uri, src_asset.uri_len); + asset.set_uri(uri); + let local_path = char_ptr_to_string(src_asset.local_path, src_asset.local_path_len); + asset.set_local_path(local_path); + let create_time = char_ptr_to_string(src_asset.create_time, src_asset.create_time_len); + asset.set_create_time(create_time); + let modify_time = char_ptr_to_string(src_asset.modify_time, src_asset.modify_time_len); + asset.set_modify_time(modify_time); + let size = char_ptr_to_string(src_asset.size, src_asset.size_len); + asset.set_size(size); + let hash = char_ptr_to_string(src_asset.hash, src_asset.hash_len); + asset.set_hash(hash); + 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_char, + name_len: usize, + alias: *const c_char, + 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 CffiHashMap); + match tables { + CffiHashMap::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_char, + 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_str() as *const c_char; + *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_char, + len: *mut usize, +) -> c_int { + if dbis_null() { + return ERRNO_NULLPTR; + } + + let db_struct = &*(db as *const types::Database); + *alias = db_struct.alias.as_str() as *const c_char; + *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 = CffiHashMap::Table(db_struct.tables.clone()); + *tables = Box::into_raw(Box::new(tb_hashmap)) as *mut basic_rust_types::HashMap; + SUCCESS +} + +/// 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_char, + name_len: usize, + alias: *const c_char, + 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 CffiVector); + match fields { + CffiVector::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_char, + 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_str() as *const c_char; + *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_char, + 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_str() as *const c_char; + *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 = CffiVector::Field(tb_struct.fields.clone()); + *fields = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector; + SUCCESS +} + +#[inline] +fn c_int_to_bool(src: c_int) -> bool { + src != 0 +} + +/// Create a Field by column name, alias, type, primary, and nullable. +#[no_mangle] +pub unsafe extern "C" fn FieldNew( + col_name: *const c_char, + col_name_len: usize, + alias: *const c_char, + alias_len: usize, + typ: c_uchar, + primary: c_int, + nullable: c_int, +) -> *mut Field { + let col_name = char_ptr_to_string(col_name, col_name_len); + let alias = char_ptr_to_string(alias, alias_len); + let typ = typ as u8; + let primary = c_int_to_bool(primary); + let nullable = c_int_to_bool(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_char, + 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_str() as *const c_char; + *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_char, + 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_str() as *const c_char; + *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_int) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *typ = fd_struct.typ as c_int; + 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 +} + +/// 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_char, + 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_char; + *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 = CffiHashMap::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 CloudInfo); + } +} + +/// Get app id from an AppInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn AppInfoGetAppId( + info: *const AppInfo, + id: *mut *const c_char, + 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_char; + *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_char, + 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_char; + *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_char, + 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_char; + *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 = CffiVector::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 Relation 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 Relation +/// instance. +#[no_mangle] +pub unsafe extern "C" fn RelationNew( + bundle_name: *const c_char, + len: usize, + relations: *mut basic_rust_types::HashMap, +) -> *mut Relation { + let name = char_ptr_to_string(bundle_name, len); + let relations = *Box::from_raw(relations as *mut CffiHashMap); + match relations { + CffiHashMap::BiTuple(re) => { + let relation = cloud_service::Relation::new(name, re); + Box::into_raw(Box::new(relation)) as *mut Relation + } + _ => null_mut(), + } +} + +/// Get bundle name from a Relation pointer. +#[no_mangle] +pub unsafe extern "C" fn RelationGetBundleName( + relation: *mut Relation, + bundle_name: *mut *const c_char, + len: *mut usize, +) -> c_int { + if relation.is_null() { + return ERRNO_NULLPTR; + } + + let relation_struct = &*(relation as *mut cloud_service::Relation); + *bundle_name = relation_struct.bundle_name.as_ptr() as *const c_char; + *len = relation_struct.bundle_name.len(); + SUCCESS +} + +/// Get relations from a Relation pointer. Parameter `relations` be updated +/// to hold the output Vector, and it should be freed by `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn RelationGetRelations( + relation: *mut Relation, + relations: *mut *const basic_rust_types::HashMap, +) -> c_int { + if relation.is_null() { + return ERRNO_NULLPTR; + } + + let relation_struct = &*(relation as *const cloud_service::Relation); + let res = relation_struct.relations(); + let hashmap = CffiHashMap::BiTuple(res.clone()); + *relations = Box::into_raw(Box::new(hashmap)) as *const basic_rust_types::HashMap; + SUCCESS +} + +/// Free a Relation pointer. +#[no_mangle] +pub unsafe extern "C" fn RelationFree(re: *mut Relation) { + if !re.is_null() { + let _ = Box::from_raw(re as *mut cloud_service::Relation); + } +} + +/// Get the next cursor from a CloudData pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudDataGetNextCursor( + data: *mut CloudData, + cursor: *mut *const c_char, + 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_char; + *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` be updated +/// to hold the output Vector, 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 = CffiVector::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(data: *mut CloudData) { + if !data.is_null() { + let _ = Box::from_raw(re 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_char, + 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_char; + *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_char, + 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_char; + *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 = CffiHashMap::Value(vb_struct.data.0.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_char, + 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_char; + *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_char, + 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_char; + *len = info_struct.app_id().len(); + SUCCESS +} diff --git a/src/cloud_extension/src/c_adapter/mod.rs b/src/cloud_extension/src/c_adapter/mod.rs index 8b137891791fe96927ad78e64b0aad7bded08bdc..d3b0b6b5c7f2a094e7f104a01aa9783e80c71d35 100644 --- a/src/cloud_extension/src/c_adapter/mod.rs +++ b/src/cloud_extension/src/c_adapter/mod.rs @@ -1 +1,8 @@ +mod basic_rust_types; +mod cloud_ext_types; +mod cloud_extension; +use std::ffi::c_int; + +pub const SUCCESS: c_int = 0; +pub const ERRNO_NULLPTR: c_int = 1; diff --git a/src/cloud_extension/src/lib.rs b/src/cloud_extension/src/lib.rs index fa8d934e248e2dc6bbfb09072229c43c5d63a459..2c1f6d13311af71f12f13abface87745f1d076a6 100644 --- a/src/cloud_extension/src/lib.rs +++ b/src/cloud_extension/src/lib.rs @@ -11,8 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(feature = "c_adapter")] -pub mod c_adapter; +//! Cloud Service Synchronization Crate, implemented in Rust. This crate is used as bridge between +//! upper C++ apk and JS cloud service. C++ will call C-ffi of Rust, and Rust will use IPC to +//! connect to JS. +// pub mod c_adapter; +/// Module of IPC Conn Serialization and Deserialization. pub mod ipc_conn; + +/// Module of Cloud Service Synchronization, providing structs of AssetLoader, CloudDatabase, +/// CloudServer and relating structs and functions. pub mod service_impl; diff --git a/src/cloud_extension/src/service_impl/asset_loader.rs b/src/cloud_extension/src/service_impl/asset_loader.rs index 2d31f7d3151a6f91824991f00c188f3a30196081..6babfa8b54e55ef7a2304543d7f0e16ff78df585 100644 --- a/src/cloud_extension/src/service_impl/asset_loader.rs +++ b/src/cloud_extension/src/service_impl/asset_loader.rs @@ -11,27 +11,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; - -use crate::ipc_conn::Asset as AssetInner; -use crate::service_impl::db_types::{Database, Table}; +use crate::ipc_conn; +use crate::ipc_conn::OperationType; use crate::service_impl::error::SyncError; -use crate::service_impl::{VBucket, Value}; +use crate::service_impl::types::{Database, RawValueBucket, Table, Value}; +use std::collections::HashMap; +/// Asset loader struct. pub struct AssetLoader<'a> { tables: &'a HashMap, - user_id: u64, + + // Keep these two fields below for future extension. + #[allow(unused)] + user_id: i32, + #[allow(unused)] bundle_name: String, } impl<'a> AssetLoader<'a> { - pub fn new_from_ipc_conn(token_id: u32, db: &Database) -> AssetLoader { - todo!(); - - // AssetLoader::new() - } - - pub fn new(user_id: u64, bundle_name: &str, db: &Database) -> Self { + /// Initialize an AssetLoader instance from user id, bundle name, and database. + /// Database can be obtained from CloudInfo and CloudServer. + #[inline] + pub fn new(user_id: i32, bundle_name: &str, db: &'a Database) -> Self { AssetLoader { tables: &db.tables, user_id, @@ -39,36 +40,227 @@ impl<'a> AssetLoader<'a> { } } - // 发消息给 JS 对端,不管文件,结束 + /// Take in table name, gid, prefix, and assets, send upload request to the other side of IPC + /// connection. Necessary information will be updated in the parameter assets. The cloud storage + /// will ask for files from source paths, and this function only returns result and relating infos. pub fn upload( &self, - src: &Asset, table_name: &str, gid: &str, - prefix: &Value, + prefix: &str, + assets: &mut RawValueBucket, ) -> Result<(), SyncError> { - todo!(); + self.upload_download_inner(table_name, gid, prefix, assets, ipc_conn::Asset::upload) } - // 发消息给 JS 对端,不管文件,结束 + /// Take in table name, gid, prefix, and assets, send download request to the other side of IPC + /// connection. Necessary information will be updated in the parameter assets. The cloud storage + /// will send files to target paths, and this function only returns result and relating infos. pub fn download( &self, table_name: &str, gid: &str, - prefix: &Value, - ) -> Result { - todo!(); + prefix: &str, + assets: &mut RawValueBucket, + ) -> Result<(), SyncError> { + self.upload_download_inner(table_name, gid, prefix, assets, ipc_conn::Asset::download) } - // 删除本地文件,用本地文件系统 - pub fn remove_local_assets(&self, assets: &VBucket) -> Result<(), SyncError> { - todo!(); + /// Remove local file according to the path in the asset passed in. + pub fn remove_local_assets(asset: &Asset) -> Result<(), SyncError> { + std::fs::remove_file(&asset.local_path)?; + Ok(()) + } + + fn upload_download_inner( + &self, + table_name: &str, + gid: &str, + prefix: &str, + assets: &mut RawValueBucket, + mut f: F, + ) -> Result<(), SyncError> + where + F: FnMut(&ipc_conn::Asset, &str) -> Result<(), ipc_conn::Error>, + { + let mut ret = Ok(()); + for value in assets.values_mut() { + match value { + Value::Assets(some_asset) => { + for asset in some_asset { + if asset.status == AssetStatus::Delete { + AssetLoader::remove_local_assets(asset)?; + } + let alias = self.get_table_alias(table_name); + let mut asset_ipc = asset.as_ipc_asset()?; + asset_ipc.prefix = prefix.to_string(); + asset_ipc.alias = alias.to_string(); + asset_ipc.gid = gid.to_string(); + match f(&asset_ipc, alias) { + Ok(_) => asset.status = AssetStatus::Normal, + Err(_) => { + asset.status = AssetStatus::Abnormal; + ret = Err(SyncError::AssetDownloadFailure); + } + } + } + } + _ => return Err(SyncError::NotAnAsset), + } + } + ret + } + + fn get_table_alias(&self, table_name: &str) -> &str { + match self.tables.get(table_name) { + None => "", + Some(t) => t.alias.as_str(), + } } } +/// Asset struct storing relating information to upload and download assets. pub struct Asset { - inner: AssetInner, - createTime: String, - modifyTime: String, + pub(crate) status: AssetStatus, + id: String, + name: String, + uri: String, + local_path: String, + create_time: String, + modify_time: String, size: String, + hash: String, +} + +impl Default for Asset { + fn default() -> Self { + Asset { + status: AssetStatus::Unknown, + id: "".to_string(), + name: "".to_string(), + uri: "".to_string(), + local_path: "".to_string(), + create_time: "".to_string(), + modify_time: "".to_string(), + size: "".to_string(), + hash: "".to_string(), + } + } +} + +impl Asset { + /// Set Asset id. + pub fn set_id(&mut self, id: String) { + self.id = id; + } + + /// Set Asset name. + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + /// Set Asset uri, a real path pointing to the Asset. + pub fn set_uri(&mut self, uri: String) { + self.uri = uri; + } + + /// Set Asset local path relative to the application sandbox. + pub fn set_local_path(&mut self, local_path: String) { + self.local_path = local_path; + } + + /// Set create time of this Asset. + pub fn set_create_time(&mut self, create_time: String) { + self.create_time = create_time; + } + + /// Set modify time of this Asset. + pub fn set_modify_time(&mut self, modify_time: String) { + self.modify_time = modify_time; + } + + /// Set size of this Asset in kb. + pub fn set_size(&mut self, size: String) { + self.size = size; + } + + /// Set hash value of this Asset. + pub fn set_hash(&mut self, hash: String) { + self.hash = hash; + } + + pub(crate) fn as_ipc_asset(&self) -> Result { + let mut asset = ipc_conn::Asset::new()?; + asset.uri = self.uri.to_string(); + asset.asset_name = self.name.clone(); + asset.operation_type = self.status.as_oper_type(); + asset.hash = self.hash.clone(); + asset.asset_id = self.id.to_string(); + asset.sub_path = self.local_path.to_string(); + Ok(asset) + } +} + +impl From<&ipc_conn::Asset> for Asset { + fn from(value: &ipc_conn::Asset) -> Self { + Asset { + status: AssetStatus::from(&value.operation_type), + id: value.asset_id.clone(), + name: value.asset_name.clone(), + uri: value.uri.clone(), + local_path: value.sub_path.clone(), + hash: value.hash.clone(), + ..Default::default() + } + } +} + +/// Struct that mark status of an Asset. +#[derive(Eq, PartialEq)] +pub enum AssetStatus { + /// Unknown status + Unknown, + + /// Normal. Everything works fine for the asset. + Normal, + + /// The asset is about to be inserted. + Insert, + + /// The asset is about to be updated. + Update, + + /// The asset is about to be deleted. + Delete, + + /// The asset is abnormal and something is wrong with it. + Abnormal, + + /// The asset is being downloading. + Downloading, + + /// The asset is butt. + Butt, +} + +impl AssetStatus { + pub(crate) fn as_oper_type(&self) -> OperationType { + match self { + AssetStatus::Delete => OperationType::Delete, + AssetStatus::Insert => OperationType::Add, + AssetStatus::Update => OperationType::Update, + _ => OperationType::None, + } + } +} + +impl From<&OperationType> for AssetStatus { + fn from(value: &OperationType) -> Self { + match value { + OperationType::Delete => AssetStatus::Delete, + OperationType::Add => AssetStatus::Insert, + OperationType::Update => AssetStatus::Update, + OperationType::None => AssetStatus::Unknown, + } + } } diff --git a/src/cloud_extension/src/service_impl/cloud_db.rs b/src/cloud_extension/src/service_impl/cloud_db.rs index 72a616fa1fbd1dd44d1983cd99ee7ae7afa145b2..b75ed93887d111a56578e96d0637f7ec0de327f8 100644 --- a/src/cloud_extension/src/service_impl/cloud_db.rs +++ b/src/cloud_extension/src/service_impl/cloud_db.rs @@ -11,75 +11,429 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::service_impl::db_types::{Database, Record}; +use crate::ipc_conn; use crate::service_impl::error::SyncError; -use crate::service_impl::VBucket; -use std::sync::MutexGuard; +use crate::service_impl::types::{ + Database, RawValueBucket, Value, CREATE_FIELD, GID_FIELD, MODIFY_FIELD, +}; +use std::collections::HashMap; +const LOCK_EXPIRE: i32 = 60 * 60; + +/// Struct of CloudDatabase pub struct CloudDatabase { + database_ipc: ipc_conn::Database, database: Database, } impl CloudDatabase { - // cloud server::connect cloud db - pub fn new_from_ipc_conn(token_id: u32, db: &Database) -> CloudDatabase { - todo!(); - - // CloudDatabase::new() - } - - pub fn new(db: &Database) -> Self { - todo!(); + /// Initialize a CloudDatabase with user id and database. This function will send IPC request + /// with user id to get relating cloud database information. + pub fn new(bundle_name: &str, database: Database) -> Result { + let database_ipc = ipc_conn::Database::new(bundle_name)?; + Ok(CloudDatabase { + database_ipc, + database, + }) } - pub fn execute(&mut self, table: &str, sql: &str, extend: &VBucket) -> Result<(), SyncError> { - todo!(); + /// Execute a sql on the cloud database. Unsupported currently. + pub fn execute( + &mut self, + _table: &str, + _sql: &str, + _extend: &RawValueBucket, + ) -> Result<(), SyncError> { + Err(SyncError::Unsupported) } + /// Insert a batch of value buckets into the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. pub fn batch_insert( &mut self, table: &str, - values: &[VBucket], - extends: &[VBucket], + values: &[RawValueBucket], + extends: &mut Vec, ) -> Result<(), SyncError> { - todo!(); + let ids = self.database_ipc.generate_ids(values.len() as u32)?; + + for (i, id) in ids.iter().enumerate() { + if i < extends.len() { + extends[i].insert(GID_FIELD.to_string(), Value::String(id.to_string())); + } else { + let mut vb = HashMap::new(); + vb.insert(GID_FIELD.to_string(), Value::String(id.to_string())); + extends.push(vb); + } + } + + let ret = self.upload(table, values, extends, true, ipc_conn::Database::insert)?; + self.write_back(extends, ret); + Ok(()) } + /// Update a batch of value buckets in the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. pub fn batch_update( &mut self, table: &str, - values: &[VBucket], - extends: &[VBucket], + values: &[RawValueBucket], + extends: &mut [RawValueBucket], ) -> Result<(), SyncError> { - todo!(); + let ret = self.upload(table, values, extends, false, ipc_conn::Database::update)?; + self.write_back(extends, ret); + Ok(()) + } + + /// Delete a batch of value buckets from the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_delete( + &mut self, + table: &str, + extends: &[RawValueBucket], + ) -> Result<(), SyncError> { + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut records = vec![]; + for ext in extends.iter() { + match ext.get(GID_FIELD) { + Some(Value::String(gid)) => { + let modify_field = match ext.get(MODIFY_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let modified_response = ipc_conn::Response { + time: modify_field, + ..Default::default() + }; + + let record_ipc = ipc_conn::ValueBucket { + id: gid.clone(), + typ: alias.clone(), + modified_info: modified_response, + is_delete: true, + is_new_create: false, + ..Default::default() + }; + + records.push(record_ipc); + } + _ => continue, + } + } + let extends = ipc_conn::ValueBuckets(records); + self.database_ipc.delete(alias, &extends)?; + Ok(()) + } + } + } + + /// Query value buckets from the cloud database, with specific target table name. Return with + /// the corresponding Cursor and ValueBuckets. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_query(&mut self, table: &str, cursor: &str) -> Result { + const QUERY_LIMIT: usize = 10; + + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut fields = vec![]; + for field in &t.fields { + fields.push(field.alias.clone()); + } + let ret = + self.database_ipc + .query_values(alias, &fields, QUERY_LIMIT as i32, cursor)?; + Ok(ret.into()) + } + } + } + + /// Send lock request to the other end through IPC message, so that users can get exclusive + /// access to the database. + /// + /// Return err if that fails. + pub fn lock(&mut self) -> Result { + if !self.database_ipc.lock_session_id.is_empty() { + return Ok(0); + } + let int = self.database_ipc.lock(LOCK_EXPIRE)?; + Ok(int) + } + + /// Send unlock request to the other end through IPC message, so that users can release exclusive + /// access to the database, and others can use it. + /// + /// Return err if that fails. + pub fn unlock(&mut self) -> Result<(), SyncError> { + if self.database_ipc.lock_session_id.is_empty() { + return Err(SyncError::SessionUnlocked); + } + self.database_ipc.unlock()?; + Ok(()) + } + + /// Send heartbeat to the other end through IPC message, so that the users can prolong exclusive + /// access to the database. If no action is taken, this access will expire after some time (by + /// default, 60 min). + pub fn heartbeat(&mut self) -> Result<(), SyncError> { + if self.database_ipc.lock_session_id.is_empty() { + return Err(SyncError::SessionUnlocked); + } + self.database_ipc.heartbeat()?; + Ok(()) } - pub fn batch_delete(&mut self, extends: &[VBucket]) -> Result<(), SyncError> { - todo!(); + fn upload( + &mut self, + table: &str, + values: &[RawValueBucket], + extends: &mut [RawValueBucket], + is_new: bool, + mut f: F, + ) -> Result, SyncError> + where + F: FnMut( + &mut ipc_conn::Database, + &str, + &ipc_conn::ValueBuckets, + &ipc_conn::ValueBuckets, + ) -> Result, + { + let mut has_asset = false; + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut values_records = vec![]; + let mut extends_records = vec![]; + for (bucket, ext) in values.iter().zip(extends.iter_mut()) { + match ext.get(GID_FIELD) { + Some(Value::String(gid)) => { + let mut value_ipc = ipc_conn::ValueBucket { + id: gid.clone(), + typ: alias.clone(), + is_delete: false, + is_new_create: is_new, + ..Default::default() + }; + + let create_field = match ext.get(CREATE_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let create_response = ipc_conn::Response { + time: create_field, + ..Default::default() + }; + let modify_field = match ext.get(MODIFY_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let modified_response = ipc_conn::Response { + time: modify_field, + ..Default::default() + }; + let ext_ipc = ipc_conn::ValueBucket { + create_info: create_response, + modified_info: modified_response, + ..Default::default() + }; + + let mut record_data = HashMap::default(); + for (key, value) in bucket { + if value.has_asset() { + has_asset = true; + ext.insert(key.to_string(), Value::Empty); + } + let field = value.as_record_field_ipc()?; + record_data.insert(key.clone(), field); + } + value_ipc.data = ipc_conn::ValueBucketData(record_data); + values_records.push(value_ipc); + extends_records.push(ext_ipc); + } + _ => continue, + } + } + let value_raw_vb = ipc_conn::ValueBuckets(values_records); + let extend_raw_vb = ipc_conn::ValueBuckets(extends_records); + let ret = f(&mut self.database_ipc, alias, &value_raw_vb, &extend_raw_vb)?; + if has_asset { + Ok(Some(ret)) + } else { + Ok(None) + } + } + } } - pub fn batch_query(&mut self, table: &str, extends: &[VBucket]) -> Result { - todo!(); + fn write_back( + &self, + extends: &mut [RawValueBucket], + upload_ret: Option, + ) { + if let Some(ret) = upload_ret { + for (extend, vbs) in extends.iter_mut().zip(ret.0.iter()) { + let fields = &vbs.data.0; + for (key, value) in extend { + if let Some(v) = fields.get(key) { + *value = Value::from(v); + } + } + } + } } +} + +/// Struct of CloudData. +pub struct CloudData { + pub(crate) next_cursor: String, + pub(crate) has_more: bool, + pub(crate) values: Vec, +} - // Unlock when MutexGuard is dropped in Rust - pub fn lock(&mut self) -> Result, SyncError> { - todo!(); +impl From for CloudData { + fn from(value: ipc_conn::CloudData) -> Self { + let mut vec = vec![]; + for v in value.values.0 { + vec.push(v.into()); + } + CloudData { + next_cursor: value.next_cursor, + has_more: value.has_more, + values: vec, + } } +} - pub fn flush(&self) -> bool { - todo!(); +impl CloudData { + /// Get next cursor from CloudData instance. + pub fn next_cursor(&self) -> &str { + &self.next_cursor } - pub fn close(&self) -> bool { - todo!(); + /// Check whether CloudData instance has more value buckets. + pub fn has_more(&self) -> bool { + self.has_more + } + + /// Get values from CloudData instance. + pub fn values(&self) -> &[ValueBucket] { + &self.values + } +} + +/// Struct of ValueBucket. +pub struct ValueBucket { + pub(crate) id: String, + pub(crate) typ: String, + pub(crate) create_info: VBInfo, + pub(crate) modified_info: VBInfo, + pub(crate) data: RawValueBucket, + pub(crate) is_delete: bool, + pub(crate) version: u32, + pub(crate) is_new_create: bool, +} + +impl From for ValueBucket { + fn from(value: ipc_conn::ValueBucket) -> Self { + let mut map = HashMap::new(); + for (key, field) in value.data.0 { + map.insert(key, Value::from(&field)); + } + ValueBucket { + id: value.id, + typ: value.typ, + create_info: value.create_info.into(), + modified_info: value.modified_info.into(), + data: map, + is_delete: value.is_delete, + version: value.version, + is_new_create: value.is_new_create, + } + } +} + +impl ValueBucket { + /// Get id of ValueBucket instance. + pub fn id(&self) -> &str { + &self.id + } + + /// Get type of ValueBucket instance. + pub fn typ(&self) -> &str { + &self.typ + } + + /// Get create info of ValueBucket instance. + pub fn create_info(&self) -> &VBInfo { + &self.create_info + } + + /// Get modified info of ValueBucket instance. + pub fn modified_info(&self) -> &VBInfo { + &self.modified_info + } + + /// Get data of ValueBucket instance. + pub fn data(&self) -> &RawValueBucket { + &self.data + } + + /// Check whether the ValueBucket instance is deleted. + pub fn is_delete(&self) -> bool { + self.is_delete + } + + /// Get version of ValueBucket instance. + pub fn version(&self) -> u32 { + self.version + } + + /// Check whether the ValueBucket instance is newly created. + pub fn is_new_create(&self) -> bool { + self.is_new_create + } +} + +/// Struct of Value Bucket infos. +pub struct VBInfo(ipc_conn::Response); + +impl From for VBInfo { + fn from(value: ipc_conn::Response) -> Self { + VBInfo(value) + } +} + +impl VBInfo { + /// Get time of VBInfo instance. + pub fn time(&self) -> i64 { + self.0.time } - pub fn heartbeat(&self) -> i32 { - todo!(); + /// Get device name of VBInfo instance. + pub fn device_name(&self) -> &str { + &self.0.device_name } - pub fn alive_time(&self) -> i64 { - todo!(); + /// Get app id of VBInfo instance. + pub fn app_id(&self) -> &str { + &self.0.app_id } } diff --git a/src/cloud_extension/src/service_impl/cloud_service.rs b/src/cloud_extension/src/service_impl/cloud_service.rs index 3db0a2ee83cab06a8de0dad393695292f8497c45..5a98fe8517e0023f6be30bd8b3877a51fce3980b 100644 --- a/src/cloud_extension/src/service_impl/cloud_service.rs +++ b/src/cloud_extension/src/service_impl/cloud_service.rs @@ -11,92 +11,262 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::ipc_conn::{AppInfo as AppInfoInner, Infos}; -use crate::service_impl::db_types::Database; -use crate::service_impl::db_types::SchemaMeta; +use crate::ipc_conn; use crate::service_impl::error::SyncError; +use crate::service_impl::types::Database; use std::collections::HashMap; +use std::ops::Deref; +/// Struct of CloudServer pub struct CloudServer { - user_id: u64, - drive_kit: &'static Infos, + user_id: i32, + connect: ipc_conn::Connect, } impl CloudServer { - // Function as RegisterCloudInstance to get instance through IPC - pub fn new(user_id: u64) -> Self { - // Establish ipc conn + /// Get a server instance through IPC, and initialize a CloudServer instance + pub fn new(user_id: i32) -> Result { + Ok(CloudServer { + user_id, + connect: ipc_conn::Connect::new()?, + }) + } + + #[inline] + fn get_cloud_info(&mut self) -> Result { + let user_info_ipc = self.connect.get_service_info(self.user_id)?; + Ok(CloudInfo::from(user_info_ipc.deref())) + } + + /// Get a cloud server info from the other end through the IPC connection + pub fn get_service_info(&mut self) -> Result { + let mut cloud_info = self.get_cloud_info()?; + cloud_info.user = self.user_id; + let app_infos = self.connect.get_app_brief_info(self.user_id)?; + for app in &app_infos.0 { + let cloud_app_info = AppInfo::from(app); + cloud_info + .apps + .insert(cloud_app_info.bundle_name.clone(), cloud_app_info); + } + Ok(cloud_info) + } + + /// Get a application schema by bundle name from the other end through the IPC connection + pub fn get_app_schema(&mut self, bundle_name: &str) -> Result { + let schema_ipc = self.connect.get_app_schema(self.user_id, bundle_name)?; + Ok(SchemaMeta::from(schema_ipc.deref())) + } + + /// Passing in relations and a callback function, if possible, send the subscription relation + /// to the cloud server and the specific database. + pub fn subscribe( + &mut self, + dbs: &HashMap>, + ) -> Result, SyncError> { + let mut relations = HashMap::new(); + for (bundle_name, dbmetas) in dbs { + let mut vec = vec![]; + for dbmeta in dbmetas { + vec.push(dbmeta.into()) + } + let dbs_ipc = ipc_conn::Databases(vec); + + let ret = self.connect.subscribe(bundle_name, &dbs_ipc)?; + for (name, result) in ret.0 { + let mut r = Relation { + bundle_name: bundle_name.clone(), + relations: HashMap::new(), + }; + for (alias, id, time) in result.0 { + r.relations.insert(alias, (id, time)); + } + relations.insert(name, r); + } + } + Ok(relations) + } - // Initialize DRIVE_KIT + /// Passing in relations and a callback function, if possible, send the unsubscription relation + /// to the cloud server and the specific database. + pub fn unsubscribe(&mut self, relations: HashMap>) -> Result<(), SyncError> { + if relations.is_empty() { + return Ok(()); + } + let unsub = ipc_conn::UnsubscriptionInfo(relations); + self.connect.unsubscribe(&unsub)?; + Ok(()) + } +} - // Get DriveKitNative +/// Struct of SchemaMeta, containing schema information on a cloud server. +pub struct SchemaMeta { + version: i32, + bundle_name: String, + databases: Vec, +} - todo!(); +impl Default for SchemaMeta { + fn default() -> Self { + SchemaMeta { + version: 0, + bundle_name: "".to_string(), + databases: vec![], + } } +} - pub fn get_service_info(&self) -> CloudInfo { - todo!(); +impl SchemaMeta { + /// Get version of SchemaMeta instance. + pub fn version(&self) -> i32 { + self.version } - pub fn get_app_schema(&self, bundle_name: &str) -> SchemaMeta { - todo!(); + /// Get bundle name of SchemaMeta instance. + pub fn bundle_name(&self) -> &str { + &self.bundle_name } - pub fn subscribe(&mut self, dbs: &HashMap>) -> Result<(), SyncError> { - todo!(); + /// Get database from a SchemaMeta instance by store id. + pub fn databases(&self) -> &[Database] { + &self.databases } +} - pub fn unsubscribe(&mut self, dbs: &HashMap>) -> Result<(), SyncError> { - todo!(); +impl From<&ipc_conn::Schema> for SchemaMeta { + fn from(value: &ipc_conn::Schema) -> Self { + let mut dbs = vec![]; + for db in &value.databases.0 { + dbs.push(Database::from(db)); + } + SchemaMeta { + version: value.version, + bundle_name: value.bundle_name.clone(), + databases: dbs, + } } } +/// Struct of CloudInfo pub struct CloudInfo { - user: i32, - id: String, - total_space: u64, - remain_space: u64, - enable_cloud: bool, - apps: HashMap, + pub(crate) user: i32, + pub(crate) id: String, + pub(crate) total_space: u64, + pub(crate) remain_space: u64, + pub(crate) enable_cloud: bool, + pub(crate) apps: HashMap, +} + +impl From<&ipc_conn::ServiceInfo> for CloudInfo { + fn from(value: &ipc_conn::ServiceInfo) -> Self { + CloudInfo { + user: 0, + id: value.account_id.clone(), + total_space: value.total_space, + remain_space: value.left_space, + enable_cloud: value.cloud_status == ipc_conn::CloudStatus::Login, + apps: Default::default(), + } + } } impl CloudInfo { - pub fn get_key(&self) -> String { - todo!() + /// Get user of CloudInfo instance. + pub fn get_user(&self) -> i32 { + self.user } - pub fn get_schema_keys(&self) -> HashMap { - todo!(); + /// Get id of CloudInfo instance. + pub fn get_id(&self) -> &str { + &self.id } - pub fn get_schema_key(&self, bundle_name: &str, instance_id: i32) -> String { - todo!(); + /// Get total space of CloudInfo instance. + pub fn get_total_space(&self) -> u64 { + self.total_space } - pub fn get_schema_prefix(&self, bundle_name: &str) -> String { - todo!(); + /// Get remain space of CloudInfo instance. + pub fn get_remain_space(&self) -> u64 { + self.remain_space } - pub fn is_valid(&self) -> bool { - !self.id.is_empty() + /// Check whether the CloudInfo instance enables cloud synchronization. + pub fn get_enable_cloud(&self) -> bool { + self.enable_cloud } - pub fn exist(&self, bundle_name: &str, instance_id: i32) -> bool { - todo!(); + /// Get app info from CloudInfo instance. + pub fn get_app_info(&self) -> &HashMap { + &self.apps } +} + +/// Struct of App Info. +pub struct AppInfo { + pub(crate) bundle_name: String, + pub(crate) app_id: String, + pub(crate) instance_id: i32, + pub(crate) cloud_switch: bool, +} - pub fn is_on(&self, bundle_name: &str, instance_id: i32) -> bool { - todo!(); +impl From<&ipc_conn::AppInfo> for AppInfo { + fn from(value: &ipc_conn::AppInfo) -> Self { + AppInfo { + bundle_name: value.bundle_name.clone(), + app_id: value.id.clone(), + instance_id: 0, + cloud_switch: value.enable_cloud, + } } +} - pub fn get_prefix(&self, fields: &[String]) -> String { - todo!(); +impl AppInfo { + /// Get bundle name of AppInfo instance. + pub fn bundle_name(&self) -> &str { + &self.bundle_name } + + /// Get app id of AppInfo instance. + pub fn app_id(&self) -> &str { + &self.app_id + } + + /// Check whether the AppInfo instance allows cloud switch. + pub fn cloud_switch(&self) -> bool { + self.cloud_switch + } + + /// Get instance id of AppInfo instance. + pub fn instance_id(&self) -> i32 { + self.instance_id + } +} + +/// Struct of Relation to represent subscription relations. +#[derive(Default)] +pub struct Relation { + pub(crate) bundle_name: String, + // Database alias as key, subscription id and time generated by the cloud as value + pub(crate) relations: HashMap, } -pub(crate) struct AppInfo { - inner: AppInfoInner, - version: u64, - instance_id: i32, - cloud_switch: bool, +impl Relation { + pub fn new(bundle_name: String, relations: HashMap) -> Relation { + Relation { + bundle_name, + relations, + } + } + + /// Get the relating bundle name of this relation instance. + pub fn bundle_name(&self) -> &str { + self.bundle_name.as_str() + } + + /// Get the inner hashmap describing relations, with database alias as key, subscription id and + /// time as value. + pub fn relations(&self) -> &HashMap { + &self.relations + } } diff --git a/src/cloud_extension/src/service_impl/db_types.rs b/src/cloud_extension/src/service_impl/db_types.rs deleted file mode 100644 index d60cbfe1760b759f08b8eeaba41e42e28598eaba..0000000000000000000000000000000000000000 --- a/src/cloud_extension/src/service_impl/db_types.rs +++ /dev/null @@ -1,76 +0,0 @@ -// 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::{Database as DatabaseInner, Lock as LockInner, Record as RecordInner}; -use std::collections::HashMap; - -pub(crate) struct SchemaMeta { - version: i32, - bundle_name: String, - databases: Vec, -} - -impl SchemaMeta { - pub fn get_database(&mut self, store_id: &str) -> &Database { - todo!(); - } - - pub fn is_valid(&self) -> bool { - todo!(); - } -} - -pub(crate) struct Database { - inner: DatabaseInner, - cloud_lock: LockInner, - name: String, - alias: String, - pub(crate) tables: HashMap, -} - -pub(crate) struct Table { - pub(crate) name: String, - alias: String, - fields: Vec, -} - -pub(crate) struct Record { - cursor: String, - data: Vec, - current_cursor: todo!(), - consumed: bool, - finished: bool, - names: Vec, -} - -pub(crate) struct Field { - colName: String, - alias: String, - typ: i32, - // By default is false - primary: bool, - // By default is true - nullable: bool, -} - -impl Default for Field { - fn default() -> Self { - Field { - colName: String::default(), - alias: String::default(), - typ: i32::default(), - primary: false, - nullable: true, - } - } -} diff --git a/src/cloud_extension/src/service_impl/error.rs b/src/cloud_extension/src/service_impl/error.rs index deb8b37b76703a8bb80582e5bba0f8833fd9cf6c..1169802b261dd0488b2a2a1b1d9f71da5f7b7e1a 100644 --- a/src/cloud_extension/src/service_impl/error.rs +++ b/src/cloud_extension/src/service_impl/error.rs @@ -11,21 +11,87 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::ipc_conn; use std::fmt::{Debug, Display, Formatter}; +/// Struct of Synchronization Error. pub enum SyncError { + /// Unknown error Unknown, + + /// Error because the input is not an asset value and can't call asset relating functions + NotAnAsset, + + /// General asset download failure + AssetDownloadFailure, + + /// General asset upload failure + AssetUploadFailure, + + /// Fail to find a table in a database + NoSuchTableInDb, + + /// Session has been unlocked and can't be unclocked again + SessionUnlocked, + + /// Unsupported functions or features + Unsupported, + + /// IPCError + IPCError(ipc_conn::Error), + + /// A bunch of IPC Errors + IPCErrors(Vec), + + /// IO Error + IO(std::io::Error), +} + +impl From for SyncError { + fn from(value: std::io::Error) -> Self { + SyncError::IO(value) + } +} + +impl From for SyncError { + fn from(value: ipc_conn::Error) -> Self { + SyncError::IPCError(value) + } +} + +impl From for SyncError { + fn from(value: ipc_conn::Errors) -> Self { + SyncError::IPCErrors(value.0) + } } impl Debug for SyncError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - todo!() + let str = match self { + SyncError::Unknown => "unknown".to_string(), + SyncError::NotAnAsset => "NotAnAsset".to_string(), + SyncError::AssetDownloadFailure => "AssetDownloadFailure".to_string(), + SyncError::AssetUploadFailure => "AssetUploadFailure".to_string(), + SyncError::NoSuchTableInDb => "NoSuchTableInDb".to_string(), + SyncError::SessionUnlocked => "SessionUnlocked".to_string(), + SyncError::Unsupported => "Unsupported".to_string(), + SyncError::IPCError(e) => e.to_string(), + SyncError::IPCErrors(es) => { + let mut ret = String::new(); + for (idx, e) in es.iter().enumerate() { + ret.push_str(&format!("\n\tidx {}: {}", idx, e)); + } + ret + } + SyncError::IO(e) => e.to_string(), + }; + write!(f, "{}", str) } } impl Display for SyncError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - todo!() + write!(f, "{:?}", self) } } diff --git a/src/cloud_extension/src/service_impl/mod.rs b/src/cloud_extension/src/service_impl/mod.rs index d4f04ee3d34ced5ce04954051c6257210cb38921..427b5d6604ad08a9fa12e40911eab4c99d310bd8 100644 --- a/src/cloud_extension/src/service_impl/mod.rs +++ b/src/cloud_extension/src/service_impl/mod.rs @@ -11,28 +11,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::ipc_conn::IpcConn; -use crate::service_impl::asset_loader::Asset; -use std::collections::HashMap; -use std::sync::Mutex; - +/// Module of AssetLoader. pub mod asset_loader; + +/// Module of CloudDatabase. pub mod cloud_db; -pub mod cloud_service; -pub mod db_types; -pub mod error; -pub(crate) static IPC_CONNS: Mutex = Mutex::new(IpcConn::new()); +/// Module of CloudService. +pub mod cloud_service; -pub(crate) enum Value { - Empty, - Int(i64), - Double(f64), - String(String), - Bool(bool), - Bytes(Vec), - Asset(Asset), - Assets(Vec), -} +/// Module of Errors. +pub mod error; -pub(crate) type VBucket = HashMap; +/// Module of Cloud Service Synchronization basic types. +pub mod types; diff --git a/src/cloud_extension/src/service_impl/types.rs b/src/cloud_extension/src/service_impl/types.rs new file mode 100644 index 0000000000000000000000000000000000000000..1e724da5701424d0f0f97245b57550ad473cc4aa --- /dev/null +++ b/src/cloud_extension/src/service_impl/types.rs @@ -0,0 +1,263 @@ +// 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; +use crate::service_impl::asset_loader::Asset; +use crate::service_impl::error::SyncError; +use std::collections::HashMap; + +pub(crate) const GID_FIELD: &str = "#_gid"; +pub(crate) const CREATE_FIELD: &str = "#_createTime"; +pub(crate) const MODIFY_FIELD: &str = "#_modifyTime"; + +/// Struct of single value. +pub enum Value { + /// Empty Value + Empty, + + /// Value containing an int + Int(i64), + + /// Value containing a double + Double(f64), + + /// Value containing a string + String(String), + + /// Value containing a bool + Bool(bool), + + /// Value containing a byte array + Bytes(Vec), + + /// Value containing an asset + Asset(Asset), + + /// Value containing an asset array + Assets(Vec), +} + +impl Value { + pub(crate) fn has_asset(&self) -> bool { + matches!(self, Value::Asset(_) | Value::Assets(_)) + } + + pub(crate) fn as_record_field_ipc(&self) -> Result { + Ok(match self { + Value::Empty => ipc_conn::Field::Null, + Value::Int(i) => ipc_conn::Field::Number(*i), + Value::Double(f) => ipc_conn::Field::Real(*f), + Value::String(s) => ipc_conn::Field::Text(s.clone()), + Value::Bool(b) => ipc_conn::Field::Bool(*b), + Value::Bytes(b) => ipc_conn::Field::Blob(b.clone()), + Value::Asset(a) => ipc_conn::Field::Asset(a.as_ipc_asset()?), + Value::Assets(a) => { + let mut ret = vec![]; + for asset in a { + ret.push(asset.as_ipc_asset()?); + } + let assets = ipc_conn::Assets(ret); + ipc_conn::Field::Assets(assets) + } + }) + } +} + +impl From<&ipc_conn::Field> for Value { + fn from(value: &ipc_conn::Field) -> Self { + match value { + ipc_conn::Field::Null => Value::Empty, + ipc_conn::Field::Number(i) => Value::Int(*i), + ipc_conn::Field::Real(f) => Value::Double(*f), + ipc_conn::Field::Text(s) => Value::String(s.clone()), + ipc_conn::Field::Bool(b) => Value::Bool(*b), + ipc_conn::Field::Blob(b) => Value::Bytes(b.clone()), + ipc_conn::Field::Asset(a) => Value::Asset(Asset::from(a)), + ipc_conn::Field::Assets(a) => { + let mut vec = vec![]; + for single_asset in &a.0 { + let asset = Asset::from(single_asset); + vec.push(asset); + } + Value::Assets(vec) + } + } + } +} + +/// Hashmap of Value name as keys, Value as values. +pub type RawValueBucket = HashMap; + +/// Struct of Database stored in the cloud server. Database can be obtained from schema meta. +pub struct Database { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) tables: HashMap, +} + +impl From<&ipc_conn::Database> for Database { + fn from(value: &ipc_conn::Database) -> Self { + let mut tables = HashMap::new(); + for t in &value.tables.0 { + tables.insert(t.table_name.clone(), Table::from(t)); + } + Database { + name: value.name.clone(), + alias: value.cloud_name.clone(), + tables, + } + } +} + +impl From<&Database> for ipc_conn::Database { + fn from(value: &Database) -> Self { + let mut tables = vec![]; + for table in value.tables.values() { + tables.push(table.into()); + } + + ipc_conn::Database { + name: value.name.clone(), + cloud_name: value.alias.to_string(), + tables: ipc_conn::SchemaOrderTables(tables), + ..Default::default() + } + } +} + +impl Database { + /// New a database instance + pub fn new(name: String, alias: String, tables: HashMap) -> Database { + Database { + name, + alias, + tables, + } + } + + /// Get local name of Database instance. + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// Get alias, or say, cloud name, of Database instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get tables of Database instance. + pub fn tables(&self) -> &HashMap { + &self.tables + } +} + +/// Struct of Table in Database. +pub struct Table { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) fields: Vec, +} + +impl Table { + /// New a table instance + pub fn new(name: String, alias: String, fields: Vec) -> Table { + Table { + name, + alias, + fields, + } + } + + /// Get name of Table instance. + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// Get name of Table instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get fields of Table instance. + pub fn fields(&self) -> &[Field] { + self.fields.as_slice() + } +} + +impl From<&ipc_conn::OrderTable> for Table { + fn from(value: &ipc_conn::OrderTable) -> Self { + Table { + name: value.table_name.clone(), + alias: value.typ.clone(), + fields: vec![], + } + } +} + +impl From<&Table> for ipc_conn::OrderTable { + fn from(value: &Table) -> Self { + ipc_conn::OrderTable { + typ: value.alias.clone(), + table_name: value.name.clone(), + } + } +} + +/// Struct of Field in Database Table. +pub struct Field { + pub(crate) col_name: String, + pub(crate) alias: String, + pub(crate) typ: u8, + // By default is false + pub(crate) primary: bool, + // By default is true + pub(crate) nullable: bool, +} + +impl Field { + /// New a field instance + pub fn new(col_name: String, alias: String, typ: u8, primary: bool, nullable: bool) -> Field { + Field { + col_name, + alias, + typ, + primary, + nullable, + } + } + + /// Get column name of Field instance. + pub fn col_name(&self) -> &str { + self.col_name.as_str() + } + + /// Get alias of Field instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get type of Field instance. + pub fn typ(&self) -> u8 { + self.typ + } + + /// Check whether this Field is primary. + pub fn primary(&self) -> bool { + self.primary + } + + /// Check whether this Field is nullable. + pub fn nullable(&self) -> bool { + self.nullable + } +}