refactor(fileio): move list-file ordering to Rust
Move the single-file --list status gates, diagnostic-versus-metadata ordering, and aggregate projection into Rust. C retains file opening and frame analysis, the private fileInfo_t layout, exact diagnostics, and row formatting behind explicit callbacks, so the user-visible behavior remains unchanged while the policy boundary is auditable. Test Plan: - git diff --cached --check - worker format, check, clippy, and serial C compilation (passed); focused Rust tests compiled but the standalone link hit the pre-existing C bridge-symbol gap
This commit is contained in:
+78
-42
@@ -4781,8 +4781,8 @@ typedef enum {
|
|||||||
info_truncated_input=4
|
info_truncated_input=4
|
||||||
} InfoError;
|
} InfoError;
|
||||||
|
|
||||||
/* Rust owns the scalar --list file-status action policy. C keeps the exact
|
/* Rust owns the --list file policy/order. C keeps the exact diagnostics,
|
||||||
* diagnostics, metadata formatting, file I/O, and private fileInfo_t layout. */
|
* metadata formatting, file I/O, and private fileInfo_t layout. */
|
||||||
enum {
|
enum {
|
||||||
FIO_RUST_LIST_FILE_ACTION_SUCCESS = 0,
|
FIO_RUST_LIST_FILE_ACTION_SUCCESS = 0,
|
||||||
FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR = 1,
|
FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR = 1,
|
||||||
@@ -4795,7 +4795,7 @@ int FIO_rust_listFileStatusAction(int status);
|
|||||||
|
|
||||||
/* Keep the private fileInfo_t layout in C. This projection contains only the
|
/* Keep the private fileInfo_t layout in C. This projection contains only the
|
||||||
* fields needed by the --list total row and crosses the Rust policy boundary
|
* fields needed by the --list total row and crosses the Rust policy boundary
|
||||||
* after FIO_listFile has finished its C-owned open/parse/display work. */
|
* after the C-owned file-info and display callbacks have run. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
U64 decompressedSize;
|
U64 decompressedSize;
|
||||||
U64 compressedSize;
|
U64 compressedSize;
|
||||||
@@ -4806,6 +4806,35 @@ typedef struct {
|
|||||||
U32 nbFiles;
|
U32 nbFiles;
|
||||||
} FIO_listFileInfoProjection_t;
|
} FIO_listFileInfoProjection_t;
|
||||||
|
|
||||||
|
typedef int (*FIO_listFileGetInfoFn)(void* opaque, const char* fileName,
|
||||||
|
fileInfo_t* info);
|
||||||
|
typedef void (*FIO_listFileDisplayStatusFn)(void* opaque, int action,
|
||||||
|
const char* fileName,
|
||||||
|
int displayLevel);
|
||||||
|
typedef void (*FIO_listFileDisplayInfoFn)(void* opaque, const char* fileName,
|
||||||
|
const fileInfo_t* info,
|
||||||
|
int displayLevel);
|
||||||
|
|
||||||
|
/* This callback state is a deliberate repr(C) bridge: filesystem access,
|
||||||
|
* frame analysis, exact diagnostics, and row formatting stay in C while Rust
|
||||||
|
* owns the per-file policy/order around them. */
|
||||||
|
typedef struct {
|
||||||
|
void* opaque;
|
||||||
|
FIO_listFileGetInfoFn getInfo;
|
||||||
|
FIO_listFileDisplayStatusFn displayStatus;
|
||||||
|
FIO_listFileDisplayInfoFn displayInfo;
|
||||||
|
} FIO_listFileCallbacks_t;
|
||||||
|
|
||||||
|
typedef char FIO_rust_list_file_callbacks_get_info_offset[
|
||||||
|
(offsetof(FIO_listFileCallbacks_t, getInfo) == sizeof(void*)) ? 1 : -1];
|
||||||
|
typedef char FIO_rust_list_file_callbacks_status_offset[
|
||||||
|
(offsetof(FIO_listFileCallbacks_t, displayStatus)
|
||||||
|
== sizeof(void*) + sizeof(FIO_listFileGetInfoFn)) ? 1 : -1];
|
||||||
|
typedef char FIO_rust_list_file_callbacks_info_offset[
|
||||||
|
(offsetof(FIO_listFileCallbacks_t, displayInfo)
|
||||||
|
== sizeof(void*) + sizeof(FIO_listFileGetInfoFn)
|
||||||
|
+ sizeof(FIO_listFileDisplayStatusFn)) ? 1 : -1];
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* opaque;
|
void* opaque;
|
||||||
int (*isStdin)(void* opaque, const char* fileName);
|
int (*isStdin)(void* opaque, const char* fileName);
|
||||||
@@ -4818,6 +4847,9 @@ typedef struct {
|
|||||||
const FIO_listFileInfoProjection_t* total);
|
const FIO_listFileInfoProjection_t* total);
|
||||||
} FIO_listMultipleFilesCallbacks_t;
|
} FIO_listMultipleFilesCallbacks_t;
|
||||||
|
|
||||||
|
int FIO_rust_listFile(const char* inputName, int displayLevel,
|
||||||
|
FIO_listFileInfoProjection_t* output,
|
||||||
|
const FIO_listFileCallbacks_t* callbacks);
|
||||||
int FIO_rust_listMultipleFiles(unsigned numFiles, const char** filenameTable,
|
int FIO_rust_listMultipleFiles(unsigned numFiles, const char** filenameTable,
|
||||||
int displayLevel,
|
int displayLevel,
|
||||||
const FIO_listMultipleFilesCallbacks_t* callbacks);
|
const FIO_listMultipleFilesCallbacks_t* callbacks);
|
||||||
@@ -4987,49 +5019,48 @@ displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
FIO_listFile(const char* inFileName, int displayLevel,
|
FIO_listFileGetInfo(void* opaque, const char* inFileName, fileInfo_t* info)
|
||||||
FIO_listFileInfoProjection_t* output)
|
|
||||||
{
|
{
|
||||||
fileInfo_t info;
|
(void)opaque;
|
||||||
memset(&info, 0, sizeof(info));
|
return (int)getFileInfo(info, inFileName);
|
||||||
{ InfoError const error = getFileInfo(&info, inFileName);
|
}
|
||||||
int const action = FIO_rust_listFileStatusAction((int)error);
|
|
||||||
switch (action) {
|
|
||||||
case FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR:
|
|
||||||
/* display error, but provide output */
|
|
||||||
DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
|
|
||||||
break;
|
|
||||||
case FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD:
|
|
||||||
DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
|
|
||||||
if (displayLevel > 2) DISPLAYOUT("\n");
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_LIST_FILE_ACTION_FILE_ERROR:
|
|
||||||
/* error occurred while opening the file */
|
|
||||||
if (displayLevel > 2) DISPLAYOUT("\n");
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT:
|
|
||||||
DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
|
|
||||||
if (displayLevel > 2) DISPLAYOUT("\n");
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_LIST_FILE_ACTION_SUCCESS:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
displayInfo(inFileName, &info, displayLevel);
|
static void
|
||||||
output->decompressedSize = info.decompressedSize;
|
FIO_listFileDisplayStatus(void* opaque, int action,
|
||||||
output->compressedSize = info.compressedSize;
|
const char* inFileName, int displayLevel)
|
||||||
output->numActualFrames = info.numActualFrames;
|
{
|
||||||
output->numSkippableFrames = info.numSkippableFrames;
|
(void)opaque;
|
||||||
output->decompUnavailable = info.decompUnavailable;
|
switch (action) {
|
||||||
output->usesCheck = info.usesCheck;
|
case FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR:
|
||||||
output->nbFiles = info.nbFiles;
|
/* display error, but provide output */
|
||||||
assert(action == FIO_RUST_LIST_FILE_ACTION_SUCCESS
|
DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
|
||||||
|| action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR);
|
break;
|
||||||
return (int)error;
|
case FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD:
|
||||||
|
DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
|
||||||
|
if (displayLevel > 2) DISPLAYOUT("\n");
|
||||||
|
break;
|
||||||
|
case FIO_RUST_LIST_FILE_ACTION_FILE_ERROR:
|
||||||
|
/* error occurred while opening the file */
|
||||||
|
if (displayLevel > 2) DISPLAYOUT("\n");
|
||||||
|
break;
|
||||||
|
case FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT:
|
||||||
|
DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
|
||||||
|
if (displayLevel > 2) DISPLAYOUT("\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FIO_listFileDisplayInfo(void* opaque, const char* inFileName,
|
||||||
|
const fileInfo_t* info, int displayLevel)
|
||||||
|
{
|
||||||
|
(void)opaque;
|
||||||
|
displayInfo(inFileName, info, displayLevel);
|
||||||
|
}
|
||||||
|
|
||||||
/* Rust owns this stateless --list input-name predicate. */
|
/* Rust owns this stateless --list input-name predicate. */
|
||||||
int FIO_listFileIsStdin(void* opaque, const char* fileName);
|
int FIO_listFileIsStdin(void* opaque, const char* fileName);
|
||||||
|
|
||||||
@@ -5062,9 +5093,14 @@ static int
|
|||||||
FIO_listFileCallback(void* opaque, const char* fileName, int displayLevel,
|
FIO_listFileCallback(void* opaque, const char* fileName, int displayLevel,
|
||||||
FIO_listFileInfoProjection_t* info)
|
FIO_listFileInfoProjection_t* info)
|
||||||
{
|
{
|
||||||
|
FIO_listFileCallbacks_t callbacks;
|
||||||
(void)opaque;
|
(void)opaque;
|
||||||
memset(info, 0, sizeof(*info));
|
memset(info, 0, sizeof(*info));
|
||||||
return FIO_listFile(fileName, displayLevel, info);
|
callbacks.opaque = NULL;
|
||||||
|
callbacks.getInfo = FIO_listFileGetInfo;
|
||||||
|
callbacks.displayStatus = FIO_listFileDisplayStatus;
|
||||||
|
callbacks.displayInfo = FIO_listFileDisplayInfo;
|
||||||
|
return FIO_rust_listFile(fileName, displayLevel, info, &callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
+294
-2
@@ -241,6 +241,7 @@ pub struct FIO_compressionParameters {
|
|||||||
|
|
||||||
/// C's private `fileInfo_t` from `programs/fileio.c`.
|
/// C's private `fileInfo_t` from `programs/fileio.c`.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct FIO_fileInfo_t {
|
pub struct FIO_fileInfo_t {
|
||||||
decompressedSize: u64,
|
decompressedSize: u64,
|
||||||
compressedSize: u64,
|
compressedSize: u64,
|
||||||
@@ -255,8 +256,8 @@ pub struct FIO_fileInfo_t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The subset of C's private `fileInfo_t` needed by the `--list` aggregate
|
/// The subset of C's private `fileInfo_t` needed by the `--list` aggregate
|
||||||
/// row. C keeps opening, parsing, and formatting each file; Rust owns only
|
/// row. C keeps opening, parsing, and formatting each file; Rust owns the
|
||||||
/// the policy that iterates and aggregates these values.
|
/// per-file and multi-file policy around those callbacks.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct FIO_listFileInfoProjection_t {
|
pub struct FIO_listFileInfoProjection_t {
|
||||||
@@ -729,6 +730,33 @@ pub type FIO_listMultipleFilesListFileFn = unsafe extern "C" fn(
|
|||||||
pub type FIO_listMultipleFilesDisplayTotalFn =
|
pub type FIO_listMultipleFilesDisplayTotalFn =
|
||||||
unsafe extern "C" fn(*mut c_void, *const FIO_listFileInfoProjection_t);
|
unsafe extern "C" fn(*mut c_void, *const FIO_listFileInfoProjection_t);
|
||||||
|
|
||||||
|
pub type FIO_listFileGetInfoFn = unsafe extern "C" fn(
|
||||||
|
*mut c_void,
|
||||||
|
*const c_char,
|
||||||
|
*mut FIO_fileInfo_t,
|
||||||
|
) -> c_int;
|
||||||
|
pub type FIO_listFileDisplayStatusFn =
|
||||||
|
unsafe extern "C" fn(*mut c_void, c_int, *const c_char, c_int);
|
||||||
|
pub type FIO_listFileDisplayInfoFn = unsafe extern "C" fn(
|
||||||
|
*mut c_void,
|
||||||
|
*const c_char,
|
||||||
|
*const FIO_fileInfo_t,
|
||||||
|
c_int,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Callbacks for the C-owned `--list` file operation.
|
||||||
|
///
|
||||||
|
/// The callbacks keep stat/open, frame analysis, exact status diagnostics, and
|
||||||
|
/// row formatting in C. Rust only orders those operations and publishes the
|
||||||
|
/// small aggregate projection consumed by the surrounding multi-file policy.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FIO_listFileCallbacks_t {
|
||||||
|
opaque: *mut c_void,
|
||||||
|
getInfo: Option<FIO_listFileGetInfoFn>,
|
||||||
|
displayStatus: Option<FIO_listFileDisplayStatusFn>,
|
||||||
|
displayInfo: Option<FIO_listFileDisplayInfoFn>,
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn list_file_status_action(status: c_int) -> c_int {
|
fn list_file_status_action(status: c_int) -> c_int {
|
||||||
match status {
|
match status {
|
||||||
@@ -750,6 +778,85 @@ pub extern "C" fn FIO_rust_listFileStatusAction(status: c_int) -> c_int {
|
|||||||
list_file_status_action(status)
|
list_file_status_action(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn project_list_file_info(info: &FIO_fileInfo_t) -> FIO_listFileInfoProjection_t {
|
||||||
|
FIO_listFileInfoProjection_t {
|
||||||
|
decompressedSize: info.decompressedSize,
|
||||||
|
compressedSize: info.compressedSize,
|
||||||
|
numActualFrames: info.numActualFrames,
|
||||||
|
numSkippableFrames: info.numSkippableFrames,
|
||||||
|
decompUnavailable: info.decompUnavailable,
|
||||||
|
usesCheck: info.usesCheck,
|
||||||
|
nbFiles: info.nbFiles,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the policy and ordering around one C-owned `--list` file operation.
|
||||||
|
///
|
||||||
|
/// C retains the stat/open and frame-analysis callback, all user-visible
|
||||||
|
/// status diagnostics, and the full metadata row formatter. Rust preserves
|
||||||
|
/// the original status gates: fatal statuses are reported and return `1`, a
|
||||||
|
/// frame error reports a diagnostic but still formats and publishes its
|
||||||
|
/// partial metadata, and success formats and publishes normally.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn FIO_rust_listFile(
|
||||||
|
input_name: *const c_char,
|
||||||
|
display_level: c_int,
|
||||||
|
output: *mut FIO_listFileInfoProjection_t,
|
||||||
|
callbacks: *const FIO_listFileCallbacks_t,
|
||||||
|
) -> c_int {
|
||||||
|
assert!(!input_name.is_null());
|
||||||
|
assert!(!output.is_null());
|
||||||
|
assert!(!callbacks.is_null());
|
||||||
|
|
||||||
|
let callbacks = unsafe { &*callbacks };
|
||||||
|
let get_info = callbacks
|
||||||
|
.getInfo
|
||||||
|
.expect("--list file-info callback is required");
|
||||||
|
let display_status = callbacks
|
||||||
|
.displayStatus
|
||||||
|
.expect("--list status-display callback is required");
|
||||||
|
let display_info = callbacks
|
||||||
|
.displayInfo
|
||||||
|
.expect("--list info-display callback is required");
|
||||||
|
|
||||||
|
let mut info = unsafe { std::mem::zeroed::<FIO_fileInfo_t>() };
|
||||||
|
let status = unsafe { get_info(callbacks.opaque, input_name, &mut info) };
|
||||||
|
let action = list_file_status_action(status);
|
||||||
|
|
||||||
|
match action {
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR
|
||||||
|
| FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD
|
||||||
|
| FIO_RUST_LIST_FILE_ACTION_FILE_ERROR
|
||||||
|
| FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT => unsafe {
|
||||||
|
display_status(callbacks.opaque, action, input_name, display_level);
|
||||||
|
},
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_SUCCESS | FIO_RUST_LIST_FILE_ACTION_INVALID => {}
|
||||||
|
_ => unreachable!("unknown --list file action {action}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
match action {
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD
|
||||||
|
| FIO_RUST_LIST_FILE_ACTION_FILE_ERROR
|
||||||
|
| FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT => 1,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_SUCCESS | FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR => {
|
||||||
|
unsafe { display_info(callbacks.opaque, input_name, &info, display_level) };
|
||||||
|
unsafe { output.write(project_list_file_info(&info)) };
|
||||||
|
status
|
||||||
|
}
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_INVALID => {
|
||||||
|
unsafe { display_info(callbacks.opaque, input_name, &info, display_level) };
|
||||||
|
unsafe { output.write(project_list_file_info(&info)) };
|
||||||
|
debug_assert!(
|
||||||
|
action == FIO_RUST_LIST_FILE_ACTION_SUCCESS
|
||||||
|
|| action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR
|
||||||
|
);
|
||||||
|
status
|
||||||
|
}
|
||||||
|
_ => unreachable!("unknown --list file action {action}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn remove_file_action(status: c_int) -> c_int {
|
fn remove_file_action(status: c_int) -> c_int {
|
||||||
match status {
|
match status {
|
||||||
@@ -3296,6 +3403,57 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ListFileTestState {
|
||||||
|
status: c_int,
|
||||||
|
info: FIO_fileInfo_t,
|
||||||
|
events: Vec<&'static str>,
|
||||||
|
status_actions: Vec<(c_int, c_int)>,
|
||||||
|
displayed: Option<(FIO_fileInfo_t, c_int)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn list_file_get_info(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
_file_name: *const c_char,
|
||||||
|
info: *mut FIO_fileInfo_t,
|
||||||
|
) -> c_int {
|
||||||
|
let state = unsafe { &mut *opaque.cast::<ListFileTestState>() };
|
||||||
|
state.events.push("get-info");
|
||||||
|
unsafe { info.write(state.info) };
|
||||||
|
state.status
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn list_file_display_status(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
action: c_int,
|
||||||
|
_file_name: *const c_char,
|
||||||
|
display_level: c_int,
|
||||||
|
) {
|
||||||
|
let state = unsafe { &mut *opaque.cast::<ListFileTestState>() };
|
||||||
|
state.events.push("display-status");
|
||||||
|
state.status_actions.push((action, display_level));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn list_file_display_info(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
_file_name: *const c_char,
|
||||||
|
info: *const FIO_fileInfo_t,
|
||||||
|
display_level: c_int,
|
||||||
|
) {
|
||||||
|
let state = unsafe { &mut *opaque.cast::<ListFileTestState>() };
|
||||||
|
state.events.push("display-info");
|
||||||
|
state.displayed = Some((unsafe { *info }, display_level));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_file_callbacks(state: &mut ListFileTestState) -> FIO_listFileCallbacks_t {
|
||||||
|
FIO_listFileCallbacks_t {
|
||||||
|
opaque: (state as *mut ListFileTestState).cast(),
|
||||||
|
getInfo: Some(list_file_get_info),
|
||||||
|
displayStatus: Some(list_file_display_status),
|
||||||
|
displayInfo: Some(list_file_display_info),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decompression")]
|
#[cfg(feature = "decompression")]
|
||||||
struct TemporaryCFile(*mut libc::FILE);
|
struct TemporaryCFile(*mut libc::FILE);
|
||||||
|
|
||||||
@@ -4031,6 +4189,140 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_file_policy_preserves_status_order_and_projection_gates() {
|
||||||
|
let file_name = CString::new("input.zst").unwrap();
|
||||||
|
let info = FIO_fileInfo_t {
|
||||||
|
decompressedSize: 100,
|
||||||
|
compressedSize: 40,
|
||||||
|
windowSize: 32,
|
||||||
|
numActualFrames: 2,
|
||||||
|
numSkippableFrames: 1,
|
||||||
|
decompUnavailable: 0,
|
||||||
|
usesCheck: 1,
|
||||||
|
checksum: [1, 2, 3, 4],
|
||||||
|
nbFiles: 1,
|
||||||
|
dictID: 7,
|
||||||
|
};
|
||||||
|
let expected_projection = project_list_file_info(&info);
|
||||||
|
let display_level = 3;
|
||||||
|
|
||||||
|
for (status, expected_action, expected_return, publishes_info) in [
|
||||||
|
(
|
||||||
|
FIO_LIST_INFO_SUCCESS,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_SUCCESS,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FIO_LIST_INFO_FRAME_ERROR,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FIO_LIST_INFO_NOT_ZSTD,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FIO_LIST_INFO_FILE_ERROR,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_FILE_ERROR,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
FIO_LIST_INFO_TRUNCATED_INPUT,
|
||||||
|
FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let mut state = ListFileTestState {
|
||||||
|
status,
|
||||||
|
info,
|
||||||
|
..ListFileTestState::default()
|
||||||
|
};
|
||||||
|
let callbacks = list_file_callbacks(&mut state);
|
||||||
|
let sentinel = FIO_listFileInfoProjection_t {
|
||||||
|
decompressedSize: 9,
|
||||||
|
compressedSize: 8,
|
||||||
|
numActualFrames: 7,
|
||||||
|
numSkippableFrames: 6,
|
||||||
|
decompUnavailable: 5,
|
||||||
|
usesCheck: 4,
|
||||||
|
nbFiles: 3,
|
||||||
|
};
|
||||||
|
let mut output = sentinel;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
FIO_rust_listFile(
|
||||||
|
file_name.as_ptr(),
|
||||||
|
display_level,
|
||||||
|
&mut output,
|
||||||
|
&callbacks,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(result, expected_return, "status {status}");
|
||||||
|
assert_eq!(
|
||||||
|
state.events,
|
||||||
|
if publishes_info {
|
||||||
|
if expected_action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR {
|
||||||
|
vec!["get-info", "display-status", "display-info"]
|
||||||
|
} else {
|
||||||
|
vec!["get-info", "display-info"]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["get-info", "display-status"]
|
||||||
|
},
|
||||||
|
"status {status} event order"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.status_actions,
|
||||||
|
if expected_action == FIO_RUST_LIST_FILE_ACTION_SUCCESS {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
vec![(expected_action, display_level)]
|
||||||
|
},
|
||||||
|
"status {status} diagnostic action"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state.displayed,
|
||||||
|
publishes_info.then_some((info, display_level)),
|
||||||
|
"status {status} display gate"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
output,
|
||||||
|
if publishes_info {
|
||||||
|
expected_projection
|
||||||
|
} else {
|
||||||
|
sentinel
|
||||||
|
},
|
||||||
|
"status {status} projection gate"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_file_callback_bridge_matches_c_layout() {
|
||||||
|
let word = size_of::<*mut c_void>();
|
||||||
|
let callback = size_of::<Option<FIO_listFileGetInfoFn>>();
|
||||||
|
|
||||||
|
assert_eq!(offset_of!(FIO_listFileCallbacks_t, opaque), 0);
|
||||||
|
assert_eq!(offset_of!(FIO_listFileCallbacks_t, getInfo), word);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(FIO_listFileCallbacks_t, displayStatus),
|
||||||
|
word + callback
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(FIO_listFileCallbacks_t, displayInfo),
|
||||||
|
word + 2 * callback
|
||||||
|
);
|
||||||
|
assert_eq!(size_of::<FIO_listFileCallbacks_t>(), word + 3 * callback);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn file_info_layout_matches_private_c_record() {
|
fn file_info_layout_matches_private_c_record() {
|
||||||
let word = size_of::<u64>();
|
let word = size_of::<u64>();
|
||||||
|
|||||||
Reference in New Issue
Block a user