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}