use std::{ffi::CString, os::raw::c_char, ptr::null_mut};
#[cfg(feature = "python")]
use pyo3::prelude::*;
use crate::{
error::Result,
tl_data::{TlData, TlDataEntry},
};
#[derive(Debug)]
#[cfg_attr(feature = "python", pyclass(from_py_object))]
pub struct DbArgs {
pub(crate) db_args: *mut *mut c_char,
_origin_args: Vec<CString>,
_ptr_vec: Vec<*mut c_char>,
}
unsafe impl Send for DbArgs {}
unsafe impl Sync for DbArgs {}
impl Clone for DbArgs {
fn clone(&self) -> Self {
let mut _origin_args = vec![];
let mut _ptr_vec = vec![];
for arg in &self._origin_args {
let c_arg = arg.clone();
_ptr_vec.push(c_arg.as_ptr().cast_mut());
_origin_args.push(c_arg);
}
_ptr_vec.push(null_mut());
let db_args = _ptr_vec.as_mut_ptr();
DbArgs {
db_args,
_origin_args,
_ptr_vec,
}
}
}
impl DbArgs {
pub fn builder() -> DbArgsBuilder {
DbArgsBuilder::default()
}
}
impl Default for DbArgs {
fn default() -> Self {
Self::builder().build().unwrap()
}
}
impl From<&DbArgs> for TlData {
fn from(db_args: &DbArgs) -> Self {
Self {
entries: db_args
._origin_args
.iter()
.map(|arg| TlDataEntry {
data_type: 0,
contents: arg.to_bytes_with_nul().to_vec(),
})
.collect(),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DbArgsBuilder(Vec<(String, Option<String>)>);
impl DbArgsBuilder {
pub fn arg(mut self, name: &str, value: Option<&str>) -> Self {
self.0.push((name.to_owned(), value.map(|s| s.to_owned())));
self
}
pub fn build(&self) -> Result<DbArgs> {
let formatted_args = self.0.clone().into_iter().map(|(name, value)| {
if let Some(value) = value {
format!("{name}={value}")
} else {
name
}
});
let mut _origin_args = vec![];
let mut _ptr_vec = vec![];
for arg in formatted_args {
let c_arg = CString::new(arg)?;
_ptr_vec.push(c_arg.as_ptr().cast_mut());
_origin_args.push(c_arg);
}
_ptr_vec.push(null_mut());
let db_args = _ptr_vec.as_mut_ptr();
Ok(DbArgs {
db_args,
_origin_args,
_ptr_vec,
})
}
}
#[cfg(test)]
mod tests {
use std::ffi::CStr;
use super::*;
#[test_log::test]
#[serial_test::serial]
fn build_empty() {
let db_args = DbArgs::builder().build().unwrap();
unsafe {
assert_eq!(*db_args.db_args, null_mut());
}
}
#[test_log::test]
#[serial_test::serial]
fn build_no_value() {
let db_args = DbArgs::builder().arg("lockiter", None).build().unwrap();
assert_eq!(
unsafe { CStr::from_ptr(*db_args.db_args).to_owned() },
CString::new("lockiter").unwrap()
);
}
#[test_log::test]
#[serial_test::serial]
fn build_with_value() {
let db_args = DbArgs::builder()
.arg("host", Some("ldap.test"))
.build()
.unwrap();
assert_eq!(
unsafe { CStr::from_ptr(*db_args.db_args).to_owned() },
CString::new("host=ldap.test").unwrap()
);
}
}