use crate::ffi::{block_on, clear_last_error, cstr_to_str, set_io_error, set_last_error};
use crate::fs::DtactFile;
use std::ffi::c_char;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_init(workers: usize) {
clear_last_error();
crate::fs::init(workers.max(1));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_create(path: *const c_char) -> *mut DtactFile {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return std::ptr::null_mut();
};
match block_on(DtactFile::create(path)) {
Ok(f) => Box::into_raw(Box::new(f)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_open(path: *const c_char) -> *mut DtactFile {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return std::ptr::null_mut();
};
match block_on(DtactFile::open(path)) {
Ok(f) => Box::into_raw(Box::new(f)),
Err(e) => {
set_io_error(&e);
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_read(
file: *mut DtactFile,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
if file.is_null() || buf.is_null() {
set_last_error("null file handle or buffer");
return -1;
}
let file = unsafe { &*file };
let scratch = vec![0u8; len];
match block_on(file.read(scratch)) {
Ok((n, data)) => {
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf, n) };
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_write(
file: *mut DtactFile,
buf: *const u8,
len: usize,
) -> isize {
clear_last_error();
if file.is_null() || buf.is_null() {
set_last_error("null file handle or buffer");
return -1;
}
let file = unsafe { &*file };
let data = unsafe { std::slice::from_raw_parts(buf, len) }.to_vec();
match block_on(file.write(data)) {
Ok((n, _)) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_read_at(
file: *mut DtactFile,
buf: *mut u8,
len: usize,
offset: u64,
) -> isize {
clear_last_error();
if file.is_null() || buf.is_null() {
set_last_error("null file handle or buffer");
return -1;
}
let file = unsafe { &*file };
let scratch = vec![0u8; len];
match block_on(file.read_at(scratch, offset)) {
Ok((n, data)) => {
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf, n) };
n as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_write_at(
file: *mut DtactFile,
buf: *const u8,
len: usize,
offset: u64,
) -> isize {
clear_last_error();
if file.is_null() || buf.is_null() {
set_last_error("null file handle or buffer");
return -1;
}
let file = unsafe { &*file };
let data = unsafe { std::slice::from_raw_parts(buf, len) }.to_vec();
match block_on(file.write_at(data, offset)) {
Ok((n, _)) => n as isize,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_sync(file: *mut DtactFile) -> i32 {
clear_last_error();
if file.is_null() {
set_last_error("null file handle");
return -1;
}
let file = unsafe { &*file };
match block_on(file.sync_all()) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_file_close(file: *mut DtactFile) {
if !file.is_null() {
drop(unsafe { Box::from_raw(file) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_copy(from: *const c_char, to: *const c_char) -> i64 {
clear_last_error();
crate::fs::init(1);
let Some(from) = (unsafe { cstr_to_str(from) }) else {
return -1;
};
let Some(to) = (unsafe { cstr_to_str(to) }) else {
return -1;
};
match block_on(crate::fs::copy(from, to)) {
Ok(n) => n as i64,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_rename(from: *const c_char, to: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(from) = (unsafe { cstr_to_str(from) }) else {
return -1;
};
let Some(to) = (unsafe { cstr_to_str(to) }) else {
return -1;
};
match block_on(crate::fs::rename(from, to)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_hard_link(src: *const c_char, dst: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(src) = (unsafe { cstr_to_str(src) }) else {
return -1;
};
let Some(dst) = (unsafe { cstr_to_str(dst) }) else {
return -1;
};
match block_on(crate::fs::hard_link(src, dst)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_symlink(src: *const c_char, dst: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(src) = (unsafe { cstr_to_str(src) }) else {
return -1;
};
let Some(dst) = (unsafe { cstr_to_str(dst) }) else {
return -1;
};
match block_on(crate::fs::symlink(src, dst)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_symlink_dir(src: *const c_char, dst: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(src) = (unsafe { cstr_to_str(src) }) else {
return -1;
};
let Some(dst) = (unsafe { cstr_to_str(dst) }) else {
return -1;
};
match block_on(crate::fs::symlink_dir(src, dst)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[cfg(windows)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_symlink_file(src: *const c_char, dst: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(src) = (unsafe { cstr_to_str(src) }) else {
return -1;
};
let Some(dst) = (unsafe { cstr_to_str(dst) }) else {
return -1;
};
match block_on(crate::fs::symlink_file(src, dst)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_read_link(
path: *const c_char,
out: *mut c_char,
out_cap: usize,
) -> isize {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::read_link(path)) {
Ok(target) => {
let s = target.to_string_lossy();
let bytes = s.as_bytes();
write_str_out(bytes, out, out_cap);
bytes.len() as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_canonicalize(
path: *const c_char,
out: *mut c_char,
out_cap: usize,
) -> isize {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::canonicalize(path)) {
Ok(resolved) => {
let s = resolved.to_string_lossy();
let bytes = s.as_bytes();
write_str_out(bytes, out, out_cap);
bytes.len() as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_read(
path: *const c_char,
buf: *mut u8,
len: usize,
) -> isize {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::read(path)) {
Ok(data) => {
if !buf.is_null() && len > 0 {
let n = data.len().min(len);
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), buf, n) };
}
data.len() as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_read_to_string(
path: *const c_char,
out: *mut c_char,
out_cap: usize,
) -> isize {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::read_to_string(path)) {
Ok(s) => {
let bytes = s.as_bytes();
write_str_out(bytes, out, out_cap);
bytes.len() as isize
}
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_write(
path: *const c_char,
buf: *const u8,
len: usize,
) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
let data = if buf.is_null() {
&[][..]
} else {
unsafe { std::slice::from_raw_parts(buf, len) }
};
match block_on(crate::fs::write(path, data)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_create_dir(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::create_dir(path)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_create_dir_all(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::create_dir_all(path)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_remove_dir(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::remove_dir(path)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_remove_file(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::remove_file(path)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_remove_dir_all(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::remove_dir_all(path)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_try_exists(path: *const c_char) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
match block_on(crate::fs::try_exists(path)) {
Ok(true) => 1,
Ok(false) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dtact_util_fs_set_readonly(path: *const c_char, readonly: i32) -> i32 {
clear_last_error();
crate::fs::init(1);
let Some(path) = (unsafe { cstr_to_str(path) }) else {
return -1;
};
let meta = match block_on(crate::fs::metadata(path)) {
Ok(m) => m,
Err(e) => {
set_io_error(&e);
return -1;
}
};
let mut perm = meta.permissions();
perm.set_readonly(readonly != 0);
match block_on(crate::fs::set_permissions(path, perm)) {
Ok(()) => 0,
Err(e) => {
set_io_error(&e);
-1
}
}
}
fn write_str_out(bytes: &[u8], out: *mut c_char, cap: usize) {
if out.is_null() || cap == 0 {
return;
}
let n = bytes.len().min(cap - 1);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr().cast::<c_char>(), out, n);
*out.add(n) = 0;
}
}