1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 use crate::service_impl::types::Database;
17 use crate::{ipc_conn, SyncResult};
18 use std::collections::HashMap;
19 use std::ops::Deref;
20 
21 /// Struct of CloudSync
22 pub struct CloudSync {
23     user_id: i32,
24     connect: ipc_conn::Connect,
25 }
26 
27 impl std::fmt::Debug for CloudSync {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result28     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29         write!(f, "CloudSync: user_id = {:?}", self.user_id)
30     }
31 }
32 
33 impl CloudSync {
34     /// Get a server instance through IPC, and initialize a CloudSync instance
new(user_id: i32) -> SyncResult<Self>35     pub fn new(user_id: i32) -> SyncResult<Self> {
36         Ok(CloudSync {
37             user_id,
38             connect: ipc_conn::Connect::new(user_id)?,
39         })
40     }
41 
42     #[inline]
get_cloud_info(&mut self) -> SyncResult<CloudInfo>43     fn get_cloud_info(&mut self) -> SyncResult<CloudInfo> {
44         let user_info_ipc = self.connect.get_service_info(self.user_id)?;
45         Ok(CloudInfo::from(user_info_ipc.deref()))
46     }
47 
48     /// Get a cloud server info from the other end through the IPC connection
get_service_info(&mut self) -> SyncResult<CloudInfo>49     pub fn get_service_info(&mut self) -> SyncResult<CloudInfo> {
50         let mut cloud_info = self.get_cloud_info()?;
51         cloud_info.user = self.user_id;
52         let app_infos = self.connect.get_app_brief_info(self.user_id)?;
53         for app in &app_infos.0 {
54             let cloud_app_info = AppInfo::from(app.1);
55             cloud_info
56                 .apps
57                 .insert(cloud_app_info.bundle_name.clone(), cloud_app_info);
58         }
59         Ok(cloud_info)
60     }
61 
62     /// Get a application schema by bundle name from the other end through the IPC connection
get_app_schema(&mut self, bundle_name: &str) -> SyncResult<SchemaMeta>63     pub fn get_app_schema(&mut self, bundle_name: &str) -> SyncResult<SchemaMeta> {
64         let schema_ipc = self.connect.get_app_schema(self.user_id, bundle_name)?;
65         Ok(SchemaMeta::from(schema_ipc.deref()))
66     }
67 
68     /// Passing in relations and a callback function, if possible, send the subscription relation
69     /// to the cloud server and the specific database.
subscribe( &mut self, dbs: &HashMap<String, Vec<Database>>, expire: i64, ) -> SyncResult<HashMap<String, RelationSet>>70     pub fn subscribe(
71         &mut self,
72         dbs: &HashMap<String, Vec<Database>>,
73         expire: i64,
74     ) -> SyncResult<HashMap<String, RelationSet>> {
75         let mut result = HashMap::new();
76         for (bundle_name, dbmetas) in dbs {
77             let mut vec = vec![];
78             for dbmeta in dbmetas {
79                 vec.push(dbmeta.try_into()?)
80             }
81             let dbs_ipc = ipc_conn::Databases(vec);
82 
83             let ret = self.connect.subscribe(expire, bundle_name, &dbs_ipc)?;
84             let (ret_expire, map) = ret.0;
85             for (bundle_name_ret, sub_result) in map {
86                 let mut r = RelationSet {
87                     bundle_name: bundle_name.clone(),
88                     expire_time: ret_expire,
89                     relations: HashMap::new(),
90                 };
91                 for (alias, id) in sub_result.0 {
92                     r.relations.insert(alias, id);
93                 }
94                 result.insert(bundle_name_ret, r);
95             }
96         }
97         Ok(result)
98     }
99 
100     /// Passing in relations and a callback function, if possible, send the unsubscription relation
101     /// to the cloud server and the specific database.
unsubscribe(&mut self, relations: &HashMap<String, Vec<String>>) -> SyncResult<()>102     pub fn unsubscribe(&mut self, relations: &HashMap<String, Vec<String>>) -> SyncResult<()> {
103         if relations.is_empty() {
104             return Ok(());
105         }
106         let unsub = ipc_conn::UnsubscriptionInfo(relations.clone());
107         self.connect.unsubscribe(&unsub)?;
108         Ok(())
109     }
110 }
111 
112 /// Struct of SchemaMeta, containing schema information on a cloud server.
113 pub struct SchemaMeta {
114     version: i32,
115     bundle_name: String,
116     databases: Vec<Database>,
117 }
118 
119 impl Default for SchemaMeta {
default() -> Self120     fn default() -> Self {
121         SchemaMeta {
122             version: 0,
123             bundle_name: "".to_string(),
124             databases: vec![],
125         }
126     }
127 }
128 
129 impl SchemaMeta {
130     /// Get version of SchemaMeta instance.
version(&self) -> i32131     pub fn version(&self) -> i32 {
132         self.version
133     }
134 
135     /// Get bundle name of SchemaMeta instance.
bundle_name(&self) -> &str136     pub fn bundle_name(&self) -> &str {
137         &self.bundle_name
138     }
139 
140     /// Get database from a SchemaMeta instance by store id.
databases(&self) -> &[Database]141     pub fn databases(&self) -> &[Database] {
142         &self.databases
143     }
144 }
145 
146 impl From<&ipc_conn::Schema> for SchemaMeta {
from(value: &ipc_conn::Schema) -> Self147     fn from(value: &ipc_conn::Schema) -> Self {
148         let mut dbs = vec![];
149         for db in &value.databases.0 {
150             dbs.push(Database::from(db));
151         }
152         SchemaMeta {
153             version: value.version,
154             bundle_name: value.bundle_name.clone(),
155             databases: dbs,
156         }
157     }
158 }
159 
160 /// Struct of CloudInfo
161 pub struct CloudInfo {
162     pub(crate) user: i32,
163     pub(crate) id: String,
164     pub(crate) total_space: u64,
165     pub(crate) remain_space: u64,
166     pub(crate) enable_cloud: bool,
167     pub(crate) apps: HashMap<String, AppInfo>,
168 }
169 
170 impl From<&ipc_conn::ServiceInfo> for CloudInfo {
from(value: &ipc_conn::ServiceInfo) -> Self171     fn from(value: &ipc_conn::ServiceInfo) -> Self {
172         CloudInfo {
173             user: value.user,
174             id: value.account_id.clone(),
175             total_space: value.total_space,
176             remain_space: value.remain_space,
177             enable_cloud: value.enable_cloud,
178             apps: Default::default(),
179         }
180     }
181 }
182 
183 impl CloudInfo {
184     /// Get user of CloudInfo instance.
get_user(&self) -> i32185     pub fn get_user(&self) -> i32 {
186         self.user
187     }
188 
189     /// Get id of CloudInfo instance.
get_id(&self) -> &str190     pub fn get_id(&self) -> &str {
191         &self.id
192     }
193 
194     /// Get total space of CloudInfo instance.
get_total_space(&self) -> u64195     pub fn get_total_space(&self) -> u64 {
196         self.total_space
197     }
198 
199     /// Get remain space of CloudInfo instance.
get_remain_space(&self) -> u64200     pub fn get_remain_space(&self) -> u64 {
201         self.remain_space
202     }
203 
204     /// Check whether the CloudInfo instance enables cloud synchronization.
get_enable_cloud(&self) -> bool205     pub fn get_enable_cloud(&self) -> bool {
206         self.enable_cloud
207     }
208 
209     /// Get app info from CloudInfo instance.
get_app_info(&self) -> &HashMap<String, AppInfo>210     pub fn get_app_info(&self) -> &HashMap<String, AppInfo> {
211         &self.apps
212     }
213 }
214 
215 /// Struct of App Info.
216 #[derive(Clone)]
217 pub struct AppInfo {
218     pub(crate) bundle_name: String,
219     pub(crate) app_id: String,
220     pub(crate) instance_id: i32,
221     pub(crate) cloud_switch: bool,
222 }
223 
224 impl From<&ipc_conn::AppInfo> for AppInfo {
from(value: &ipc_conn::AppInfo) -> Self225     fn from(value: &ipc_conn::AppInfo) -> Self {
226         AppInfo {
227             bundle_name: value.bundle_name.clone(),
228             app_id: value.app_id.clone(),
229             instance_id: value.instance_id,
230             cloud_switch: value.cloud_switch,
231         }
232     }
233 }
234 
235 impl AppInfo {
236     /// Get bundle name of AppInfo instance.
bundle_name(&self) -> &str237     pub fn bundle_name(&self) -> &str {
238         &self.bundle_name
239     }
240 
241     /// Get app id of AppInfo instance.
app_id(&self) -> &str242     pub fn app_id(&self) -> &str {
243         &self.app_id
244     }
245 
246     /// Check whether the AppInfo instance allows cloud switch.
cloud_switch(&self) -> bool247     pub fn cloud_switch(&self) -> bool {
248         self.cloud_switch
249     }
250 
251     /// Get instance id of AppInfo instance.
instance_id(&self) -> i32252     pub fn instance_id(&self) -> i32 {
253         self.instance_id
254     }
255 }
256 
257 /// Struct of Relation to represent subscription relations.
258 #[derive(Default, Clone)]
259 pub struct RelationSet {
260     pub(crate) bundle_name: String,
261     pub(crate) expire_time: i64,
262     // Database alias as key, subscription id generated by the cloud as value
263     pub(crate) relations: HashMap<String, String>,
264 }
265 
266 impl RelationSet {
267     /// New a RelationSet.
new( bundle_name: String, expire_time: i64, relations: HashMap<String, String>, ) -> RelationSet268     pub fn new(
269         bundle_name: String,
270         expire_time: i64,
271         relations: HashMap<String, String>,
272     ) -> RelationSet {
273         RelationSet {
274             bundle_name,
275             expire_time,
276             relations,
277         }
278     }
279 
280     /// Get the relating bundle name of this RelationSet instance.
bundle_name(&self) -> &str281     pub fn bundle_name(&self) -> &str {
282         self.bundle_name.as_str()
283     }
284 
285     /// Get expire time of this RelationSet instance.
expire_time(&self) -> i64286     pub fn expire_time(&self) -> i64 {
287         self.expire_time
288     }
289 
290     /// Get the inner hashmap describing relations, with database alias as key, subscription id and
291     /// time as value.
relations(&self) -> &HashMap<String, String>292     pub fn relations(&self) -> &HashMap<String, String> {
293         &self.relations
294     }
295 }
296