pub mod local_db_model;
pub mod local_db_state;
mod test;
mod app_response;
use crate::local_db_model::LocalDbModel;
use crate::local_db_state::AppDbState;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use log::{info, warn};
use crate::app_response::AppResponse;
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn create_db(name: *const c_char) -> *mut AppDbState {
if name.is_null() {
warn!("Null name pointer passed to create_db");
return std::ptr::null_mut();
}
let name_str = match unsafe { CStr::from_ptr(name).to_str() } {
Ok(s) => s,
Err(e) => {
warn!("Invalid UTF-8 in name parameter: {e}");
return std::ptr::null_mut();
}
};
let db_path = format!("./{name_str}");
if std::path::Path::new(&format!("{db_path}.lmdb")).exists() {
info!("Database already exists, opening existing database");
} else {
info!("Creating new database");
}
let state = AppDbState::init(db_path);
info!("Database initialized");
match state {
Ok(response) => Box::into_raw(Box::new(response)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn push_data(state: *mut AppDbState, json_ptr: *const c_char) -> *const c_char {
let state = match unsafe { state.as_ref() } {
Some(s) => s,
None => {
let error = AppResponse::BadRequest("Null state pointer".to_string());
return response_to_c_string(&error);
}
};
let json_str = match c_ptr_to_string(json_ptr, "JSON") {
Ok(response) => response,
Err(err) => return err
};
let model: LocalDbModel = match serde_json::from_str(&json_str) {
Ok(m) => m,
Err(e) => {
let error = AppResponse::SerializationError(format!("Invalid JSON: {e}"));
return response_to_c_string(&error);
}
};
match state.push(model) {
Ok(result_model) => {
match serde_json::to_string(&result_model) {
Ok(json) => {
let success = AppResponse::Ok(json);
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::SerializationError(format!("Failed to serialize result: {e}"));
response_to_c_string(&error)
}
}
},
Err(e) => response_to_c_string(&e)
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn get_by_id(state: *mut AppDbState, id: *const c_char) -> *const c_char {
if state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to get_by_id".to_string());
return response_to_c_string(&error);
}
if id.is_null() {
let error = AppResponse::BadRequest("Null id pointer passed to get_by_id".to_string());
return response_to_c_string(&error);
}
let state = unsafe { &*state };
let id_str = match c_ptr_to_string(id, "id") {
Ok(json) => json,
Err(error_ptr) => return error_ptr,
};
match state.get_by_id(&id_str) {
Ok(Some(model)) => {
match serde_json::to_string(&model) {
Ok(json) => {
let success = AppResponse::Ok(json);
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::SerializationError(format!("Error serializing to JSON: {e:?}"));
response_to_c_string(&error)
}
}
},
Ok(None) => {
let error = AppResponse::NotFound(format!("No model found with id: {id_str}"));
response_to_c_string(&error)
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn get_all(state: *mut AppDbState) -> *const c_char {
if state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to get_all".to_string());
return response_to_c_string(&error);
}
let state = unsafe { &*state };
match state.get() {
Ok(models) => {
match serde_json::to_string(&models) {
Ok(json) => {
let success = AppResponse::Ok(json);
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::SerializationError(format!("Error serializing models: {e:?}"));
response_to_c_string(&error)
}
}
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn update_data(state: *mut AppDbState, json_ptr: *const c_char) -> *const c_char {
if state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to update_data".to_string());
return response_to_c_string(&error);
}
if json_ptr.is_null() {
let error = AppResponse::BadRequest("Null JSON pointer passed to update_data".to_string());
return response_to_c_string(&error);
}
let json_str = match c_ptr_to_string(json_ptr, "JSON") {
Ok(json) => json,
Err(error_ptr) => return error_ptr,
};
let model: LocalDbModel = match serde_json::from_str(&json_str) {
Ok(m) => m,
Err(e) => {
let error = AppResponse::SerializationError(format!("Error deserializing JSON: {e:?}"));
return response_to_c_string(&error);
}
};
let state = unsafe { &*state };
match state.update(model) {
Ok(Some(updated_model)) => {
match serde_json::to_string(&updated_model) {
Ok(json) => {
let success = AppResponse::Ok(json);
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::SerializationError(format!("Error serializing updated model: {e:?}"));
response_to_c_string(&error)
}
}
},
Ok(None) => {
let error = AppResponse::NotFound("Model not found for update".to_string());
response_to_c_string(&error)
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn delete_by_id(db_state: *mut AppDbState, id: *const c_char) -> *const c_char {
if db_state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to delete_by_id".to_string());
return response_to_c_string(&error);
}
if id.is_null() {
let error = AppResponse::BadRequest("Null id pointer passed to delete_by_id".to_string());
return response_to_c_string(&error);
}
let id_str = match c_ptr_to_string(id, "id") {
Ok(id) => id,
Err(error_ptr) => return error_ptr,
};
let db_state = unsafe { &mut *db_state };
match db_state.delete_by_id(&id_str) {
Ok(true) => {
let success = AppResponse::Ok("Record deleted successfully".to_string());
response_to_c_string(&success)
},
Ok(false) => {
let not_found = AppResponse::NotFound(format!("No record found with id: {id_str}"));
response_to_c_string(¬_found)
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn clear_all_records(db_state: *mut AppDbState) -> *const c_char {
if db_state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to clear_all_records".to_string());
return response_to_c_string(&error);
}
let db_state = unsafe { &*db_state };
match db_state.clear_all_records() {
Ok(_) => {
let success = AppResponse::Ok("All records cleared successfully".to_string());
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn reset_database(db_state: *mut AppDbState, name_ptr: *const c_char) -> *const c_char {
if db_state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to reset_database".to_string());
return response_to_c_string(&error);
}
if name_ptr.is_null() {
let error = AppResponse::BadRequest("Null name pointer passed to reset_database".to_string());
return response_to_c_string(&error);
}
let name = match c_ptr_to_string(name_ptr, "name") {
Ok(name) => name,
Err(error_ptr) => return error_ptr,
};
let db_state = unsafe { &mut *db_state };
match db_state.reset_database(&name) {
Ok(_) => {
let success = AppResponse::Ok(format!("Database '{name}' was reset successfully"));
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::DatabaseError(format!("Error resetting database: {e:?}"));
response_to_c_string(&error)
}
}
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn close_database(db_state: *mut AppDbState) -> *const c_char {
if db_state.is_null() {
let error = AppResponse::BadRequest("Null state pointer passed to close_database".to_string());
return response_to_c_string(&error);
}
let db_state = unsafe { &mut *db_state };
match db_state.close_database() {
Ok(_) => {
let success = AppResponse::Ok("Database connection closed successfully".to_string());
response_to_c_string(&success)
},
Err(e) => {
let error = AppResponse::from(e);
response_to_c_string(&error)
}
}
}
fn response_to_c_string(response: &AppResponse) -> *const c_char {
let json = match serde_json::to_string(response) {
Ok(j) => j,
Err(e) => {
warn!("Error serializing response: {e}");
return std::ptr::null();
}
};
match CString::new(json) {
Ok(c_str) => c_str.into_raw(),
Err(e) => {
warn!("Error creating CString: {e}");
std::ptr::null()
}
}
}
fn c_ptr_to_string(ptr: *const c_char, field_name: &str) -> Result<String, *const c_char> {
if ptr.is_null() {
let error = AppResponse::BadRequest(format!("Null {field_name} pointer"));
return Err(response_to_c_string(&error));
}
match unsafe { CStr::from_ptr(ptr).to_str() } {
Ok(s) => Ok(s.to_string()),
Err(e) => {
let error = AppResponse::BadRequest(format!("Invalid UTF-8 in {field_name}: {e}"));
Err(response_to_c_string(&error))
}
}
}