napi_package_template/
lib.rs

1#![deny(clippy::all)]
2
3#[macro_use]
4extern crate napi_derive;
5
6// #[napi]
7// pub fn sum(a: i32, b: i32) -> i32 {
8//   a + b
9// }
10
11pub mod configration;
12pub mod logger;
13
14use logger::LoggerConfig;
15use once_cell::sync::OnceCell;
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18use tikv_client::{raw::Client, Config};
19static TIKV: OnceCell<Client> = OnceCell::new();
20
21#[derive(Clone, Serialize, Deserialize, Debug)]
22#[napi(object)]
23pub struct TikvConnParams {
24  pub tlsclusterenabled: bool,
25  pub sslcacerti: String,
26  pub sslclientcerti: String,
27  pub sslclientkeycerti: String,
28  pub host: String,
29}
30
31#[derive(Debug, Serialize, Deserialize)]
32#[napi(object)]
33
34pub struct BatchResponse {
35  pub keys: Vec<String>,
36  pub values: Option<Vec<String>>,
37}
38
39#[napi]
40pub async fn init_client(tikv_conn_param: Option<TikvConnParams>) -> Result<String, napi::Error> {
41  match create_client(tikv_conn_param).await {
42    Ok(_res) => Ok(String::from("Client Created")),
43    Err(err) => Err(napi::Error::from_reason(format!("Error in Client Creation- {}",err.to_string()))),
44  }
45}
46
47pub async fn create_client(tikv_conn_param: Option<TikvConnParams>) -> Result<Client, napi::Error> {
48  let cleint = TIKV.get();
49  match cleint {
50    Some(_client) => Ok(TIKV.get().unwrap().to_owned()),
51    None => {
52      let config = Config::default();
53      let tls_cluster_enabled = false;
54      println!("In tls cluster enable{}:-", tls_cluster_enabled);
55      if tls_cluster_enabled {
56        let with_security_config = config.to_owned().with_security(
57          tikv_conn_param.clone().unwrap().sslcacerti,
58          tikv_conn_param.clone().unwrap().sslclientcerti,
59          tikv_conn_param.clone().unwrap().sslclientkeycerti,
60        );
61        let client = Client::new_with_config(
62          vec![tikv_conn_param.clone().unwrap().host],
63          with_security_config,
64        )
65        .await;
66        match client {
67          Ok(client) => {
68            let new_client: Client = client.with_atomic_for_cas();
69            TIKV.get_or_init(|| new_client.to_owned());
70            Ok(new_client)
71          }
72          Err(error) => Err(napi::Error::from_reason(error.to_string())),
73        }
74      } else {
75        // let security = config.with_security("ca_path", "cert_path", "key_path");
76        let client = Client::new_with_config(vec![tikv_conn_param.unwrap().host], config).await;
77        match client {
78          Ok(client) => {
79            let new_client: Client = client.with_atomic_for_cas();
80            TIKV.get_or_init(|| new_client.to_owned());
81            Ok(new_client)
82          }
83          Err(error) => Err(napi::Error::from_reason(error.to_string())),
84        }
85      }
86    }
87  }
88}
89
90#[napi]
91pub fn startLogger() {
92  // You can use handle to change logger config at runtime
93  // just call startLogger() in main.rs and you can use log4rs in all your Project-crate.
94  let Global_logs_config = LoggerConfig::create_Global_logs_config();
95  let handle = log4rs::init_config(Global_logs_config).unwrap();
96}
97
98#[napi]
99pub async fn get_single_record(
100  key: String,
101  project_name: Option<String>,
102) -> Result<serde_json::Value, napi::Error> {
103  let client = create_client(None).await;
104  match client {
105    Ok(client) => {
106      let mut new_key = key;
107      if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
108        let res =
109          get_project_level_key_with_global_prefix(project_name.as_ref().unwrap(), &new_key);
110        match res {
111          Ok(res) => {
112            new_key = res;
113          }
114          Err(error) => {
115            return Err(napi::Error::from_reason(error.to_string()));
116          }
117        }
118      }
119      let value = client.get(new_key).await; // Returns a `tikv_client::Error` on failure.
120      match value {
121        Ok(value) => match value {
122          Some(value) => {
123            let res = String::from_utf8(value).map_err(|e| napi::Error::from_reason(format!("UTF-8 conversion error: {}", e)))?;
124            let parsed_json:Value = serde_json::from_str(&res)
125              .map_err(|e| napi::Error::from_reason(format!("JSON parsing error: {}", e)))?;
126            return Ok(parsed_json);
127          }
128          None => {
129            Err(napi::Error::from_reason("Key Does Not Exits".to_string()))
130            // return Err("Key Does Not Exits".to_string());
131          }
132        },
133        Err(error) => {
134          return Err(napi::Error::from_reason(error.to_string()));
135        }
136      }
137    }
138    Err(error) => return Err(napi::Error::from_reason(error.to_string())),
139  }
140}
141
142#[napi]
143pub async fn add_single_record(
144  key: String,
145  value: Value,
146  old_value: Option<String>,
147  project_name: Option<String>,
148) -> Result<String, napi::Error> {
149  let client = create_client(None).await;
150  match client {
151    Ok(client) => {
152      let mut new_key = key.to_owned();
153      if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
154        let res =
155          get_project_level_key_with_global_prefix(project_name.as_ref().unwrap(), &new_key);
156        match res {
157          Ok(res) => {
158            new_key = res;
159          }
160          Err(error) => {
161            return Err(napi::Error::from_reason(error.to_string()));
162          }
163        }
164      }
165      if !old_value.is_none() && old_value.to_owned().unwrap().len() > 0 {
166        let new_eqa = client
167          .compare_and_swap(
168            new_key.to_owned(),
169            Some(old_value.clone().unwrap().as_bytes().to_vec()),
170            value.to_string().as_bytes().to_vec(),
171          )
172          .await;
173        match new_eqa {
174          Ok((_new_val, _flag)) => {
175            if !_flag {
176              return Err(napi::Error::from_reason("Could not Update".to_string()));
177            }
178            return Ok(String::from(
179              "Record Updated With CAS For Key".to_owned() + &key,
180            ));
181          }
182          Err(error) => {
183            return Err(napi::Error::from_reason(error.to_string()));
184          }
185        }
186      } else {
187        let client_res = client.put(new_key.to_owned(), value.to_string()).await; // Returns a `tikv_client::Error` on failure.
188        match client_res {
189          Ok(_res) => {
190            return Ok(String::from("New Record Added With Key".to_owned() + &key));
191          }
192          Err(error) => {
193            return Err(napi::Error::from_reason(error.to_string()));
194          }
195        }
196      }
197    }
198    Err(error) => {
199      return Err(napi::Error::from_reason(error.to_string()));
200    }
201  }
202}
203
204// pub async fn get_batch_using_scan(
205//     start: String,
206//     end: String,
207//     batch_size: i32,
208//     only_keys: bool,
209//     project_name: Option<String>,
210// ) -> Result<String, String> {
211//     let client = create_client(None).await;
212//     match client {
213//         Ok(client) => {
214//             let mut start_key = start;
215//             let mut end_key = end.to_owned();
216//             let mut prefix_value = "".to_string();
217//             if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
218//                 let start_key_with_project = get_project_level_key_with_global_prefix(
219//                     project_name.as_ref().unwrap(),
220//                     &start_key,
221//                 );
222//                 match start_key_with_project {
223//                     Ok(start_key_with_project) => {
224//                         let end_key_with_project = get_project_level_key_with_global_prefix(
225//                             project_name.as_ref().unwrap(),
226//                             &end_key,
227//                         );
228//                         match end_key_with_project {
229//                             Ok(mut end_key_with_project) => {
230//                                 start_key = start_key_with_project;
231//                                 if end.len() == 0 {
232//                                     end_key_with_project.push_str("~");
233//                                 }
234//                                 end_key_with_project.push_str("\\0");
235//                                 end_key = end_key_with_project;
236//                                 prefix_value = format!("k{}_", project_name.unwrap().to_lowercase())
237//                             }
238//                             Err(error) => {
239//                                 return Err(napi::Error::from_reason(error.to_string()));
240//                             }
241//                         }
242//                     }
243//                     Err(error) => {
244//                         return Err(napi::Error::from_reason(error.to_string()));
245//                     }
246//                 }
247//             }
248//             if only_keys {
249//                 let scan_result = client
250//                     .scan_keys((start_key..=end_key).into_inner(), batch_size as u32)
251//                     .await;
252//                 match scan_result {
253//                     Ok(scan_result) => {
254//                         let string_keys: Vec<String> = scan_result
255//                             .iter()
256//                             .map(|key| {
257//                                 String::from_utf8_lossy(key.as_ref().into())
258//                                     .to_string()
259//                                     .replace(&prefix_value, "")
260//                             })
261//                             .collect();
262//                         let res = serde_json::to_string(&BatchResponse {
263//                             keys: string_keys,
264//                             values: None,
265//                         });
266//                         match res {
267//                             Ok(res) => {
268//                                 return Ok(res);
269//                             }
270//                             Err(error) => return Err(napi::Error::from_reason(error.to_string())),
271//                         }
272//                     }
273//                     Err(error) => return Err(napi::Error::from_reason(error.to_string())),
274//                 }
275//             } else {
276//                 let scan = client
277//                     .scan((start_key..=end_key).into_inner(), batch_size as u32)
278//                     .await;
279//                 match scan {
280//                     Ok(scan) => {
281//                         let string_keys: Vec<String> = scan
282//                             .iter()
283//                             .map(|key| {
284//                                 String::from_utf8_lossy(key.0.as_ref().into())
285//                                     .to_string()
286//                                     .replace(&prefix_value, "")
287//                             })
288//                             .collect();
289//                         let string_values: Vec<String> = scan
290//                             .iter()
291//                             .map(|key| String::from_utf8_lossy(&key.1.to_vec()).to_string())
292//                             .collect();
293//                         let res = serde_json::to_string(&BatchResponse {
294//                             keys: string_keys,
295//                             values: Some(string_values),
296//                         });
297//                         match res {
298//                             Ok(res) => {
299//                                 return Ok(res);
300//                             }
301//                             Err(error) => return Err(napi::Error::from_reason(error.to_string())),
302//                         }
303//                     }
304//                     Err(error) => return Err(napi::Error::from_reason(error.to_string())),
305//                 }
306//             }
307//         }
308//         Err(error) => {
309//             return Err(napi::Error::from_reason(error.to_string()));
310//         }
311//     }
312// }
313
314// pub async fn delete_single_record(
315//     key: String,
316//     project_name: Option<String>,
317// ) -> Result<String, String> {
318//     let client = create_client(None).await;
319//     match client {
320//         Ok(client) => {
321//             let mut new_key = key.to_owned();
322//             if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
323//                 let res = get_project_level_key_with_global_prefix(
324//                     project_name.as_ref().unwrap(),
325//                     &new_key,
326//                 );
327//                 match res {
328//                     Ok(res) => {
329//                         new_key = res;
330//                     }
331//                     Err(error) => {
332//                         return Err(napi::Error::from_reason(error.to_string()));
333//                     }
334//                 }
335//             }
336//             let delete_res = client.delete(new_key.to_owned()).await;
337//             match delete_res {
338//                 Ok(_delete_res) => {
339//                     return Ok(String::from("Record Deleted With Key".to_owned() + &key));
340//                 }
341//                 Err(error) => {
342//                     return Err(napi::Error::from_reason(error.to_string()));
343//                 }
344//             }
345//         }
346//         Err(error) => {
347//             return Err(napi::Error::from_reason(error.to_string()));
348//         }
349//     }
350// }
351
352pub fn get_project_level_key_with_global_prefix(
353  project: &str,
354  key: &str,
355) -> Result<String, napi::Error> {
356  if project.trim().is_empty() {
357    return Err(napi::Error::from_reason(
358      "tikv:project cannot be empty".to_string(),
359    ));
360  }
361  if key.trim().contains("~") {
362    return Err(napi::Error::from_reason(
363      "tikv:invalid character in key: ~".to_string(),
364    ));
365  }
366  let key_with_project_name = format!("k{}_{}", project.trim().to_lowercase(), key.trim());
367  Ok(key_with_project_name)
368}
369
370// pub fn caste
371#[derive(Debug)]
372pub struct ReturnError {
373  pub error: String,
374}