#![deny(clippy::all)]
#[macro_use]
extern crate napi_derive;
pub mod configration;
pub mod logger;
use logger::LoggerConfig;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tikv_client::{raw::Client, Config};
static TIKV: OnceCell<Client> = OnceCell::new();
#[derive(Clone, Serialize, Deserialize, Debug)]
#[napi(object)]
pub struct TikvConnParams {
pub tlsclusterenabled: bool,
pub sslcacerti: String,
pub sslclientcerti: String,
pub sslclientkeycerti: String,
pub host: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[napi(object)]
pub struct BatchResponse {
pub keys: Vec<String>,
pub values: Option<Vec<String>>,
}
#[napi]
pub async fn init_client(tikv_conn_param: Option<TikvConnParams>) -> Result<String, napi::Error> {
match create_client(tikv_conn_param).await {
Ok(_res) => Ok(String::from("Client Created")),
Err(err) => Err(napi::Error::from_reason(format!("Error in Client Creation- {}",err.to_string()))),
}
}
pub async fn create_client(tikv_conn_param: Option<TikvConnParams>) -> Result<Client, napi::Error> {
let cleint = TIKV.get();
match cleint {
Some(_client) => Ok(TIKV.get().unwrap().to_owned()),
None => {
let config = Config::default();
let tls_cluster_enabled = false;
println!("In tls cluster enable{}:-", tls_cluster_enabled);
if tls_cluster_enabled {
let with_security_config = config.to_owned().with_security(
tikv_conn_param.clone().unwrap().sslcacerti,
tikv_conn_param.clone().unwrap().sslclientcerti,
tikv_conn_param.clone().unwrap().sslclientkeycerti,
);
let client = Client::new_with_config(
vec![tikv_conn_param.clone().unwrap().host],
with_security_config,
)
.await;
match client {
Ok(client) => {
let new_client: Client = client.with_atomic_for_cas();
TIKV.get_or_init(|| new_client.to_owned());
Ok(new_client)
}
Err(error) => Err(napi::Error::from_reason(error.to_string())),
}
} else {
let client = Client::new_with_config(vec![tikv_conn_param.unwrap().host], config).await;
match client {
Ok(client) => {
let new_client: Client = client.with_atomic_for_cas();
TIKV.get_or_init(|| new_client.to_owned());
Ok(new_client)
}
Err(error) => Err(napi::Error::from_reason(error.to_string())),
}
}
}
}
}
#[napi]
pub fn startLogger() {
let Global_logs_config = LoggerConfig::create_Global_logs_config();
let handle = log4rs::init_config(Global_logs_config).unwrap();
}
#[napi]
pub async fn get_single_record(
key: String,
project_name: Option<String>,
) -> Result<serde_json::Value, napi::Error> {
let client = create_client(None).await;
match client {
Ok(client) => {
let mut new_key = key;
if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
let res =
get_project_level_key_with_global_prefix(project_name.as_ref().unwrap(), &new_key);
match res {
Ok(res) => {
new_key = res;
}
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
}
let value = client.get(new_key).await; match value {
Ok(value) => match value {
Some(value) => {
let res = String::from_utf8(value).map_err(|e| napi::Error::from_reason(format!("UTF-8 conversion error: {}", e)))?;
let parsed_json:Value = serde_json::from_str(&res)
.map_err(|e| napi::Error::from_reason(format!("JSON parsing error: {}", e)))?;
return Ok(parsed_json);
}
None => {
Err(napi::Error::from_reason("Key Does Not Exits".to_string()))
}
},
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
}
Err(error) => return Err(napi::Error::from_reason(error.to_string())),
}
}
#[napi]
pub async fn add_single_record(
key: String,
value: Value,
old_value: Option<String>,
project_name: Option<String>,
) -> Result<String, napi::Error> {
let client = create_client(None).await;
match client {
Ok(client) => {
let mut new_key = key.to_owned();
if !project_name.is_none() && project_name.as_ref().unwrap().len() > 0 {
let res =
get_project_level_key_with_global_prefix(project_name.as_ref().unwrap(), &new_key);
match res {
Ok(res) => {
new_key = res;
}
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
}
if !old_value.is_none() && old_value.to_owned().unwrap().len() > 0 {
let new_eqa = client
.compare_and_swap(
new_key.to_owned(),
Some(old_value.clone().unwrap().as_bytes().to_vec()),
value.to_string().as_bytes().to_vec(),
)
.await;
match new_eqa {
Ok((_new_val, _flag)) => {
if !_flag {
return Err(napi::Error::from_reason("Could not Update".to_string()));
}
return Ok(String::from(
"Record Updated With CAS For Key".to_owned() + &key,
));
}
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
} else {
let client_res = client.put(new_key.to_owned(), value.to_string()).await; match client_res {
Ok(_res) => {
return Ok(String::from("New Record Added With Key".to_owned() + &key));
}
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
}
}
Err(error) => {
return Err(napi::Error::from_reason(error.to_string()));
}
}
}
pub fn get_project_level_key_with_global_prefix(
project: &str,
key: &str,
) -> Result<String, napi::Error> {
if project.trim().is_empty() {
return Err(napi::Error::from_reason(
"tikv:project cannot be empty".to_string(),
));
}
if key.trim().contains("~") {
return Err(napi::Error::from_reason(
"tikv:invalid character in key: ~".to_string(),
));
}
let key_with_project_name = format!("k{}_{}", project.trim().to_lowercase(), key.trim());
Ok(key_with_project_name)
}
#[derive(Debug)]
pub struct ReturnError {
pub error: String,
}