use crate::pool::Pool;
use crate::status::{apr_result, Status};
use crate::strings::{pstrdup, BStr, PoolString};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
pub fn path_to_cstring<'a, P: AsRef<Path>>(
path: P,
pool: &'a Pool<'a>,
) -> Result<PoolString<'a>, Status> {
let path = path.as_ref();
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
let bytes = path.as_os_str().as_bytes();
let path_str = String::from_utf8_lossy(bytes);
pstrdup(&path_str, pool).map_err(|_| Status::BadArgument)
}
#[cfg(windows)]
{
let path_str = path.to_string_lossy();
pstrdup(&path_str, pool).map_err(|_| Status::BadArgument)
}
}
pub unsafe fn cstring_to_pathbuf(ptr: *const core::ffi::c_char) -> PathBuf {
if ptr.is_null() {
return PathBuf::new();
}
let bstr = BStr::from_ptr(ptr);
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
let os_str = OsStr::from_bytes(bstr.as_bytes());
PathBuf::from(os_str)
}
#[cfg(windows)]
{
let path_str = bstr.to_string_lossy();
PathBuf::from(path_str.as_ref())
}
}
pub fn normalize_path<P: AsRef<Path>>(path: P, pool: &Pool<'_>) -> Result<PathBuf, Status> {
let path_cstr = path_to_cstring(path, pool)?;
unsafe {
let mut normalized_ptr: *const core::ffi::c_char = std::ptr::null();
let status = apr_sys::apr_filepath_merge(
&mut normalized_ptr as *mut _ as *mut *mut core::ffi::c_char,
std::ptr::null(), path_cstr.as_ptr(),
apr_sys::APR_FILEPATH_SECUREROOT as i32,
pool.as_mut_ptr(),
);
apr_result(status)?;
Ok(cstring_to_pathbuf(normalized_ptr))
}
}
pub fn is_absolute<P: AsRef<Path>>(_path: P, pool: &Pool<'_>) -> Result<bool, Status> {
let _pool = pool; Ok(_path.as_ref().is_absolute())
}
pub fn join_paths<P1: AsRef<Path>, P2: AsRef<Path>>(
base: P1,
path: P2,
pool: &Pool<'_>,
) -> Result<PathBuf, Status> {
let base_cstr = path_to_cstring(base, pool)?;
let path_cstr = path_to_cstring(path, pool)?;
unsafe {
let mut joined_ptr: *const core::ffi::c_char = std::ptr::null();
let status = apr_sys::apr_filepath_merge(
&mut joined_ptr as *mut _ as *mut *mut core::ffi::c_char,
base_cstr.as_ptr(),
path_cstr.as_ptr(),
0, pool.as_mut_ptr(),
);
apr_result(status)?;
Ok(cstring_to_pathbuf(joined_ptr))
}
}
pub fn get_cwd(pool: &Pool<'_>) -> Result<PathBuf, Status> {
unsafe {
let mut cwd_ptr: *mut core::ffi::c_char = std::ptr::null_mut();
let status = apr_sys::apr_filepath_get(
&mut cwd_ptr,
0, pool.as_mut_ptr(),
);
apr_result(status)?;
Ok(cstring_to_pathbuf(cwd_ptr))
}
}
pub fn set_cwd<P: AsRef<Path>>(path: P, pool: &Pool<'_>) -> Result<(), Status> {
let path_cstr = path_to_cstring(path, pool)?;
unsafe {
let status = apr_sys::apr_filepath_set(path_cstr.as_ptr(), pool.as_mut_ptr());
apr_result(status)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn test_path_conversion() {
let pool = Pool::new();
let path = Path::new("/tmp/test/file.txt");
let pool_string = path_to_cstring(path, &pool).unwrap();
assert!(pool_string.as_str().unwrap().contains("tmp"));
assert!(pool_string.as_str().unwrap().contains("file.txt"));
}
#[test]
fn test_get_cwd() {
let pool = Pool::new();
let cwd = get_cwd(&pool).unwrap();
assert!(!cwd.as_os_str().is_empty());
}
}