From c522906d077f77bc70f31f92079d768a89a94613 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Tue, 21 Jul 2026 07:35:53 +0200 Subject: [PATCH] refactor(cli): move list status policy to Rust Move the scalar InfoError-to-action mapping used by FIO_listFile into Rust and reuse the same classifier while aggregating multi-file list results. C retains file opening/parsing, exact diagnostics, metadata formatting, private fileInfo_t state, and the public result values; add layout and mapping coverage. Test Plan: git diff --cached --check; focused Rust mapping test added; full capped verification will run after the MT and dictionary workers are integrated. --- programs/fileio.c | 28 +++++++++++---- rust/src/fileio_prefs.rs | 75 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 4d579d5cf..2337cdc1a 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -4743,6 +4743,18 @@ typedef enum { info_truncated_input=4 } InfoError; +/* Rust owns the scalar --list file-status action policy. C keeps the exact + * diagnostics, metadata formatting, file I/O, and private fileInfo_t layout. */ +enum { + FIO_RUST_LIST_FILE_ACTION_SUCCESS = 0, + FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR = 1, + FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD = 2, + FIO_RUST_LIST_FILE_ACTION_FILE_ERROR = 3, + FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT = 4, + FIO_RUST_LIST_FILE_ACTION_INVALID = 5 +}; +int FIO_rust_listFileStatusAction(int status); + /* 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 * after FIO_listFile has finished its C-owned open/parse/display work. */ @@ -4943,24 +4955,25 @@ FIO_listFile(const char* inFileName, int displayLevel, fileInfo_t info; memset(&info, 0, sizeof(info)); { InfoError const error = getFileInfo(&info, inFileName); - switch (error) { - case info_frame_error: + 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 info_not_zstd: + 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 info_file_error: + case FIO_RUST_LIST_FILE_ACTION_FILE_ERROR: /* error occurred while opening the file */ if (displayLevel > 2) DISPLAYOUT("\n"); return 1; - case info_truncated_input: + case FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT: DISPLAYOUT("File \"%s\" is truncated \n", inFileName); if (displayLevel > 2) DISPLAYOUT("\n"); return 1; - case info_success: + case FIO_RUST_LIST_FILE_ACTION_SUCCESS: default: break; } @@ -4973,7 +4986,8 @@ FIO_listFile(const char* inFileName, int displayLevel, output->decompUnavailable = info.decompUnavailable; output->usesCheck = info.usesCheck; output->nbFiles = info.nbFiles; - assert(error == info_success || error == info_frame_error); + assert(action == FIO_RUST_LIST_FILE_ACTION_SUCCESS + || action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR); return (int)error; } } diff --git a/rust/src/fileio_prefs.rs b/rust/src/fileio_prefs.rs index 681db3100..48ab2ec8a 100644 --- a/rust/src/fileio_prefs.rs +++ b/rust/src/fileio_prefs.rs @@ -30,6 +30,15 @@ const FIO_MULTI_FILES_ACTION_QUIET_ABORT: c_int = 4; const FIO_MULTI_FILES_ACTION_CONFIRM: c_int = 5; const FIO_LIST_INFO_SUCCESS: c_int = 0; const FIO_LIST_INFO_FRAME_ERROR: c_int = 1; +const FIO_LIST_INFO_NOT_ZSTD: c_int = 2; +const FIO_LIST_INFO_FILE_ERROR: c_int = 3; +const FIO_LIST_INFO_TRUNCATED_INPUT: c_int = 4; +pub const FIO_RUST_LIST_FILE_ACTION_SUCCESS: c_int = 0; +pub const FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR: c_int = 1; +pub const FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD: c_int = 2; +pub const FIO_RUST_LIST_FILE_ACTION_FILE_ERROR: c_int = 3; +pub const FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT: c_int = 4; +pub const FIO_RUST_LIST_FILE_ACTION_INVALID: c_int = 5; const UTIL_FILESIZE_UNKNOWN: u64 = u64::MAX; const COMPRESSED_FILE_EXTENSIONS: &[&[u8]] = &[ b".zst", @@ -704,6 +713,27 @@ pub type FIO_listMultipleFilesListFileFn = unsafe extern "C" fn( pub type FIO_listMultipleFilesDisplayTotalFn = unsafe extern "C" fn(*mut c_void, *const FIO_listFileInfoProjection_t); +#[inline] +fn list_file_status_action(status: c_int) -> c_int { + match status { + FIO_LIST_INFO_SUCCESS => FIO_RUST_LIST_FILE_ACTION_SUCCESS, + FIO_LIST_INFO_FRAME_ERROR => FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR, + FIO_LIST_INFO_NOT_ZSTD => FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD, + FIO_LIST_INFO_FILE_ERROR => FIO_RUST_LIST_FILE_ACTION_FILE_ERROR, + FIO_LIST_INFO_TRUNCATED_INPUT => FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT, + _ => FIO_RUST_LIST_FILE_ACTION_INVALID, + } +} + +/// Map the C-owned `InfoError` result to the scalar `--list` action. +/// +/// C retains the exact diagnostics, file opening/parsing, metadata formatting, +/// and private `fileInfo_t` layout. Rust owns only this status policy. +#[no_mangle] +pub extern "C" fn FIO_rust_listFileStatusAction(status: c_int) -> c_int { + list_file_status_action(status) +} + /// Callback projection for the C-owned `--list` file operations. #[repr(C)] pub struct FIO_listMultipleFilesCallbacks_t { @@ -2203,7 +2233,10 @@ pub unsafe extern "C" fn FIO_rust_listMultipleFiles( let mut info = FIO_listFileInfoProjection_t::default(); let status = unsafe { list_file(callbacks.opaque, file_name, displayLevel, &mut info) }; error |= status; - if status == FIO_LIST_INFO_SUCCESS || status == FIO_LIST_INFO_FRAME_ERROR { + let action = list_file_status_action(status); + if action == FIO_RUST_LIST_FILE_ACTION_SUCCESS + || action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR + { total = add_list_file_info(total, info); } } @@ -2539,6 +2572,46 @@ mod tests { } } + #[test] + fn list_file_status_action_preserves_cli_mapping_and_abi() { + for (status, action) in [ + ( + FIO_LIST_INFO_SUCCESS, + FIO_RUST_LIST_FILE_ACTION_SUCCESS, + ), + ( + FIO_LIST_INFO_FRAME_ERROR, + FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR, + ), + ( + FIO_LIST_INFO_NOT_ZSTD, + FIO_RUST_LIST_FILE_ACTION_NOT_ZSTD, + ), + ( + FIO_LIST_INFO_FILE_ERROR, + FIO_RUST_LIST_FILE_ACTION_FILE_ERROR, + ), + ( + FIO_LIST_INFO_TRUNCATED_INPUT, + FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT, + ), + (-1, FIO_RUST_LIST_FILE_ACTION_INVALID), + (5, FIO_RUST_LIST_FILE_ACTION_INVALID), + (c_int::MAX, FIO_RUST_LIST_FILE_ACTION_INVALID), + ] { + assert_eq!( + list_file_status_action(status), + action, + "unexpected action for --list status {status}" + ); + assert_eq!( + FIO_rust_listFileStatusAction(status), + action, + "C ABI disagrees for --list status {status}" + ); + } + } + #[test] fn source_exclusion_preserves_case_sensitive_suffixes_and_null_policy() { let cases = [