refactor(fileio): move concatenation policy ordering to Rust
Move the multi-input single-output decision sequence out of FIO_multiFilesConcatWarning while preserving the C-owned CLI boundary. The previous wrapper classified fatal remove cases, emitted the concatenation warning, disabled --rm, reclassified the action, and then selected quiet abort or confirmation in C. Add a repr(C) callback projection so Rust owns only that scalar ordering while C continues to own exact diagnostics, the confirmation prompt, FIO_prefs_t mutation, and all private state. The Rust bridge re-runs the action after the C disable-remove callback with the same has-output/remove arguments as the original wrapper. C and Rust assert the callback layout, and focused tests cover callback order, fatal/quiet paths, and ABI offsets. Existing scalar action tests remain in place. Test Plan: - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -B -C programs -j1 fileio.o` — passed; only pre-existing suffixList C++-compat warnings appeared. - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --tests --no-deps` — passed. - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 RUSTFLAGS='-C link-arg=/tmp/zstd_rust_test_bridges.o' cargo test --manifest-path rust/cli/Cargo.toml --lib 'fileio_prefs::tests::multi_files_concat_'` — 8 passed. - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo build --manifest-path rust/cli/Cargo.toml --lib` — passed; the archive exports `FIO_rust_multiFilesConcatWarning`. - `cargo +nightly fmt --manifest-path rust/cli/Cargo.toml -- --check` — not clean due pre-existing formatting drift in unchanged Rust code; no bulk formatting was applied. - Full native/upstream/fuzzer suites were not run by request.
This commit is contained in:
+129
-60
@@ -292,22 +292,62 @@ typedef char FIO_rust_compression_params_size[
|
||||
int FIO_shouldDisplayFileSummary(const FIO_ctx_t* fCtx);
|
||||
int FIO_shouldDisplayMultipleFileSummary(const FIO_ctx_t* fCtx);
|
||||
|
||||
enum {
|
||||
FIO_RUST_MULTI_FILES_ACTION_PROCEED = 0,
|
||||
FIO_RUST_MULTI_FILES_ACTION_FATAL_STDOUT_REMOVE = 1,
|
||||
FIO_RUST_MULTI_FILES_ACTION_FATAL_TEST_REMOVE = 2,
|
||||
FIO_RUST_MULTI_FILES_ACTION_DISABLE_REMOVE = 3,
|
||||
FIO_RUST_MULTI_FILES_ACTION_QUIET_ABORT = 4,
|
||||
FIO_RUST_MULTI_FILES_ACTION_CONFIRM = 5
|
||||
};
|
||||
int FIO_rust_multiFilesConcatAction(int nbFilesTotal,
|
||||
int hasStdoutOutput,
|
||||
int testMode,
|
||||
int hasOutputFile,
|
||||
int removeSrcFile,
|
||||
int overwrite,
|
||||
int displayLevel,
|
||||
int displayLevelCutoff);
|
||||
typedef void (*FIO_rust_multi_files_concat_fatal_fn)(void* opaque);
|
||||
typedef void (*FIO_rust_multi_files_concat_warning_fn)(void* opaque,
|
||||
int hasStdoutOutput,
|
||||
const char* outFileName);
|
||||
typedef void (*FIO_rust_multi_files_concat_disable_remove_fn)(void* opaque);
|
||||
typedef void (*FIO_rust_multi_files_concat_quiet_abort_fn)(void* opaque);
|
||||
typedef int (*FIO_rust_multi_files_concat_confirmation_fn)(void* opaque,
|
||||
int hasStdinInput);
|
||||
typedef struct {
|
||||
void* opaque;
|
||||
FIO_rust_multi_files_concat_fatal_fn fatalStdoutRemove;
|
||||
FIO_rust_multi_files_concat_fatal_fn fatalTestRemove;
|
||||
FIO_rust_multi_files_concat_warning_fn displayWarning;
|
||||
FIO_rust_multi_files_concat_disable_remove_fn disableRemove;
|
||||
FIO_rust_multi_files_concat_quiet_abort_fn displayQuietAbort;
|
||||
FIO_rust_multi_files_concat_confirmation_fn requireConfirmation;
|
||||
} FIO_rust_multi_files_concat_callbacks_t;
|
||||
typedef char FIO_rust_multi_files_concat_callback_sizes[
|
||||
(sizeof(FIO_rust_multi_files_concat_fatal_fn) == sizeof(void*)
|
||||
&& sizeof(FIO_rust_multi_files_concat_warning_fn) == sizeof(void*)
|
||||
&& sizeof(FIO_rust_multi_files_concat_disable_remove_fn) == sizeof(void*)
|
||||
&& sizeof(FIO_rust_multi_files_concat_quiet_abort_fn) == sizeof(void*)
|
||||
&& sizeof(FIO_rust_multi_files_concat_confirmation_fn) == sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_opaque_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, opaque) == 0) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_fatal_stdout_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, fatalStdoutRemove)
|
||||
== sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_fatal_test_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, fatalTestRemove)
|
||||
== 2 * sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_warning_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, displayWarning)
|
||||
== 3 * sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_disable_remove_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, disableRemove)
|
||||
== 4 * sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_quiet_abort_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, displayQuietAbort)
|
||||
== 5 * sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_confirmation_offset[
|
||||
(offsetof(FIO_rust_multi_files_concat_callbacks_t, requireConfirmation)
|
||||
== 6 * sizeof(void*)) ? 1 : -1];
|
||||
typedef char FIO_rust_multi_files_concat_callbacks_size[
|
||||
(sizeof(FIO_rust_multi_files_concat_callbacks_t) == 7 * sizeof(void*)) ? 1 : -1];
|
||||
int FIO_rust_multiFilesConcatWarning(int nbFilesTotal,
|
||||
int hasStdoutOutput,
|
||||
int testMode,
|
||||
int hasOutputFile,
|
||||
int removeSrcFile,
|
||||
int overwrite,
|
||||
int displayLevel,
|
||||
int displayLevelCutoff,
|
||||
int hasStdinInput,
|
||||
const char* outFileName,
|
||||
const FIO_rust_multi_files_concat_callbacks_t* callbacks);
|
||||
|
||||
/*-*************************************
|
||||
* Parameters: Initialization
|
||||
@@ -1087,7 +1127,8 @@ static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
|
||||
}
|
||||
|
||||
/* FIO_multiFilesConcatWarning() :
|
||||
* This function handles logic when processing multiple files with -o or -c, displaying the appropriate warnings/prompts.
|
||||
* This function supplies the C-owned diagnostics and prompt callbacks for the
|
||||
* Rust policy/order wrapper used when processing multiple files with -o or -c.
|
||||
* Returns 1 if the console should abort, 0 if console should proceed.
|
||||
*
|
||||
* If output is stdout or test mode is active, check that `--rm` disabled.
|
||||
@@ -1101,60 +1142,88 @@ static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
|
||||
* If -f is specified or if output is stdout, just proceed.
|
||||
* If output is set with -o, prompt for confirmation.
|
||||
*/
|
||||
static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
|
||||
typedef struct {
|
||||
const FIO_ctx_t* fCtx;
|
||||
FIO_prefs_t* prefs;
|
||||
} FIO_multiFilesConcatCallbackContext_t;
|
||||
|
||||
static void
|
||||
FIO_multiFilesConcatFatalStdoutRemove(void* opaque)
|
||||
{
|
||||
int action = FIO_rust_multiFilesConcatAction(
|
||||
fCtx->nbFilesTotal, fCtx->hasStdoutOutput, prefs->testMode,
|
||||
outFileName != NULL, prefs->removeSrcFile, prefs->overwrite,
|
||||
g_display_prefs.displayLevel, displayLevelCutoff);
|
||||
(void)opaque;
|
||||
/* this should not happen ; hard fail, to protect user's data
|
||||
* note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
|
||||
EXM_THROW(43, "It's not allowed to remove input files when processed output is piped to stdout. "
|
||||
"This scenario is not supposed to be possible. "
|
||||
"This is a programming error. File an issue for it to be fixed.");
|
||||
}
|
||||
|
||||
if (action == FIO_RUST_MULTI_FILES_ACTION_FATAL_STDOUT_REMOVE)
|
||||
/* this should not happen ; hard fail, to protect user's data
|
||||
* note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
|
||||
EXM_THROW(43, "It's not allowed to remove input files when processed output is piped to stdout. "
|
||||
"This scenario is not supposed to be possible. "
|
||||
"This is a programming error. File an issue for it to be fixed.");
|
||||
if (action == FIO_RUST_MULTI_FILES_ACTION_FATAL_TEST_REMOVE)
|
||||
/* this should not happen ; hard fail, to protect user's data
|
||||
* note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
|
||||
EXM_THROW(43, "Test mode shall not remove input files! "
|
||||
"This scenario is not supposed to be possible. "
|
||||
"This is a programming error. File an issue for it to be fixed.");
|
||||
static void
|
||||
FIO_multiFilesConcatFatalTestRemove(void* opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
/* this should not happen ; hard fail, to protect user's data
|
||||
* note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
|
||||
EXM_THROW(43, "Test mode shall not remove input files! "
|
||||
"This scenario is not supposed to be possible. "
|
||||
"This is a programming error. File an issue for it to be fixed.");
|
||||
}
|
||||
|
||||
if (fCtx->nbFilesTotal == 1) return 0;
|
||||
assert(fCtx->nbFilesTotal > 1);
|
||||
|
||||
if (!outFileName || prefs->testMode) return 0;
|
||||
|
||||
if (fCtx->hasStdoutOutput) {
|
||||
static void
|
||||
FIO_multiFilesConcatDisplayWarning(void* opaque, int hasStdoutOutput,
|
||||
const char* outFileName)
|
||||
{
|
||||
(void)opaque;
|
||||
if (hasStdoutOutput) {
|
||||
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
|
||||
} else {
|
||||
DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
|
||||
}
|
||||
DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
|
||||
}
|
||||
|
||||
/* multi-input into single output : --rm is not allowed */
|
||||
if (action == FIO_RUST_MULTI_FILES_ACTION_DISABLE_REMOVE) {
|
||||
DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
|
||||
prefs->removeSrcFile = 0;
|
||||
action = FIO_rust_multiFilesConcatAction(
|
||||
fCtx->nbFilesTotal, fCtx->hasStdoutOutput, prefs->testMode,
|
||||
1, 0, prefs->overwrite, g_display_prefs.displayLevel,
|
||||
displayLevelCutoff);
|
||||
}
|
||||
|
||||
if (action == FIO_RUST_MULTI_FILES_ACTION_PROCEED) return 0;
|
||||
static void
|
||||
FIO_multiFilesConcatDisableRemove(void* opaque)
|
||||
{
|
||||
FIO_multiFilesConcatCallbackContext_t* const context =
|
||||
(FIO_multiFilesConcatCallbackContext_t*)opaque;
|
||||
DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
|
||||
context->prefs->removeSrcFile = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
FIO_multiFilesConcatDisplayQuietAbort(void* opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
/* multiple files concatenated into single destination file using -o without -f */
|
||||
if (action == FIO_RUST_MULTI_FILES_ACTION_QUIET_ABORT) {
|
||||
/* quiet mode => no prompt => fail automatically */
|
||||
DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
|
||||
DISPLAYLEVEL(1, "Aborting. \n");
|
||||
return 1;
|
||||
}
|
||||
/* normal mode => prompt */
|
||||
assert(action == FIO_RUST_MULTI_FILES_ACTION_CONFIRM);
|
||||
return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
|
||||
/* quiet mode => no prompt => fail automatically */
|
||||
DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
|
||||
DISPLAYLEVEL(1, "Aborting. \n");
|
||||
}
|
||||
|
||||
static int
|
||||
FIO_multiFilesConcatRequireConfirmation(void* opaque, int hasStdinInput)
|
||||
{
|
||||
(void)opaque;
|
||||
return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", hasStdinInput);
|
||||
}
|
||||
|
||||
static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
|
||||
{
|
||||
FIO_multiFilesConcatCallbackContext_t context = { fCtx, prefs };
|
||||
FIO_rust_multi_files_concat_callbacks_t callbacks;
|
||||
callbacks.opaque = &context;
|
||||
callbacks.fatalStdoutRemove = FIO_multiFilesConcatFatalStdoutRemove;
|
||||
callbacks.fatalTestRemove = FIO_multiFilesConcatFatalTestRemove;
|
||||
callbacks.displayWarning = FIO_multiFilesConcatDisplayWarning;
|
||||
callbacks.disableRemove = FIO_multiFilesConcatDisableRemove;
|
||||
callbacks.displayQuietAbort = FIO_multiFilesConcatDisplayQuietAbort;
|
||||
callbacks.requireConfirmation = FIO_multiFilesConcatRequireConfirmation;
|
||||
return FIO_rust_multiFilesConcatWarning(
|
||||
fCtx->nbFilesTotal, fCtx->hasStdoutOutput, prefs->testMode,
|
||||
outFileName != NULL, prefs->removeSrcFile, prefs->overwrite,
|
||||
g_display_prefs.displayLevel, displayLevelCutoff, fCtx->hasStdinInput,
|
||||
outFileName, &callbacks);
|
||||
}
|
||||
|
||||
#ifndef ZSTD_NOCOMPRESS
|
||||
|
||||
@@ -2202,6 +2202,151 @@ fn multi_files_concat_action(
|
||||
}
|
||||
}
|
||||
|
||||
pub type FIO_multiFilesConcatFatalFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type FIO_multiFilesConcatWarningFn = unsafe extern "C" fn(*mut c_void, c_int, *const c_char);
|
||||
pub type FIO_multiFilesConcatDisableRemoveFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type FIO_multiFilesConcatQuietAbortFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type FIO_multiFilesConcatConfirmationFn = unsafe extern "C" fn(*mut c_void, c_int) -> c_int;
|
||||
|
||||
/// C-owned diagnostics, preference mutation, and prompt callbacks for the
|
||||
/// multiple-input concatenation policy.
|
||||
#[repr(C)]
|
||||
pub struct FIO_multiFilesConcatCallbacks_t {
|
||||
opaque: *mut c_void,
|
||||
fatalStdoutRemove: Option<FIO_multiFilesConcatFatalFn>,
|
||||
fatalTestRemove: Option<FIO_multiFilesConcatFatalFn>,
|
||||
displayWarning: Option<FIO_multiFilesConcatWarningFn>,
|
||||
disableRemove: Option<FIO_multiFilesConcatDisableRemoveFn>,
|
||||
displayQuietAbort: Option<FIO_multiFilesConcatQuietAbortFn>,
|
||||
requireConfirmation: Option<FIO_multiFilesConcatConfirmationFn>,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(size_of::<Option<FIO_multiFilesConcatFatalFn>>() == size_of::<usize>());
|
||||
assert!(size_of::<Option<FIO_multiFilesConcatWarningFn>>() == size_of::<usize>());
|
||||
assert!(size_of::<Option<FIO_multiFilesConcatDisableRemoveFn>>() == size_of::<usize>());
|
||||
assert!(size_of::<Option<FIO_multiFilesConcatQuietAbortFn>>() == size_of::<usize>());
|
||||
assert!(size_of::<Option<FIO_multiFilesConcatConfirmationFn>>() == size_of::<usize>());
|
||||
assert!(offset_of!(FIO_multiFilesConcatCallbacks_t, opaque) == 0);
|
||||
assert!(offset_of!(FIO_multiFilesConcatCallbacks_t, fatalStdoutRemove) == size_of::<usize>());
|
||||
assert!(offset_of!(FIO_multiFilesConcatCallbacks_t, fatalTestRemove) == 2 * size_of::<usize>());
|
||||
assert!(offset_of!(FIO_multiFilesConcatCallbacks_t, displayWarning) == 3 * size_of::<usize>());
|
||||
assert!(offset_of!(FIO_multiFilesConcatCallbacks_t, disableRemove) == 4 * size_of::<usize>());
|
||||
assert!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, displayQuietAbort) == 5 * size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, requireConfirmation) == 6 * size_of::<usize>()
|
||||
);
|
||||
assert!(size_of::<FIO_multiFilesConcatCallbacks_t>() == 7 * size_of::<usize>());
|
||||
};
|
||||
|
||||
/// Run the policy/order around multiple inputs sharing one output.
|
||||
///
|
||||
/// Rust owns only the scalar action sequence, including the reclassification
|
||||
/// after `--rm` is disabled. C callbacks retain exact diagnostics, the
|
||||
/// private preference mutation, the prompt, and fatal exit behavior.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_multiFilesConcatWarning(
|
||||
nb_files_total: c_int,
|
||||
has_stdout_output: c_int,
|
||||
test_mode: c_int,
|
||||
has_output_file: c_int,
|
||||
remove_src_file: c_int,
|
||||
overwrite: c_int,
|
||||
display_level: c_int,
|
||||
display_level_cutoff: c_int,
|
||||
has_stdin_input: c_int,
|
||||
out_file_name: *const c_char,
|
||||
callbacks: *const FIO_multiFilesConcatCallbacks_t,
|
||||
) -> c_int {
|
||||
assert!(!callbacks.is_null());
|
||||
let callbacks = unsafe { &*callbacks };
|
||||
let action = multi_files_concat_action(
|
||||
nb_files_total,
|
||||
has_stdout_output,
|
||||
test_mode,
|
||||
has_output_file,
|
||||
remove_src_file,
|
||||
overwrite,
|
||||
display_level,
|
||||
display_level_cutoff,
|
||||
);
|
||||
|
||||
match action {
|
||||
FIO_MULTI_FILES_ACTION_FATAL_STDOUT_REMOVE => {
|
||||
let fatal = callbacks
|
||||
.fatalStdoutRemove
|
||||
.expect("multi-file stdout-removal callback is required");
|
||||
unsafe { fatal(callbacks.opaque) };
|
||||
return 1;
|
||||
}
|
||||
FIO_MULTI_FILES_ACTION_FATAL_TEST_REMOVE => {
|
||||
let fatal = callbacks
|
||||
.fatalTestRemove
|
||||
.expect("multi-file test-removal callback is required");
|
||||
unsafe { fatal(callbacks.opaque) };
|
||||
return 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if nb_files_total == 1 {
|
||||
return 0;
|
||||
}
|
||||
debug_assert!(nb_files_total > 1);
|
||||
|
||||
if out_file_name.is_null() || test_mode != 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let display_warning = callbacks
|
||||
.displayWarning
|
||||
.expect("multi-file warning callback is required");
|
||||
unsafe { display_warning(callbacks.opaque, has_stdout_output, out_file_name) };
|
||||
|
||||
let action = if action == FIO_MULTI_FILES_ACTION_DISABLE_REMOVE {
|
||||
let disable_remove = callbacks
|
||||
.disableRemove
|
||||
.expect("multi-file remove-disable callback is required");
|
||||
unsafe { disable_remove(callbacks.opaque) };
|
||||
multi_files_concat_action(
|
||||
nb_files_total,
|
||||
has_stdout_output,
|
||||
test_mode,
|
||||
1,
|
||||
0,
|
||||
overwrite,
|
||||
display_level,
|
||||
display_level_cutoff,
|
||||
)
|
||||
} else {
|
||||
action
|
||||
};
|
||||
|
||||
match action {
|
||||
FIO_MULTI_FILES_ACTION_PROCEED => 0,
|
||||
FIO_MULTI_FILES_ACTION_QUIET_ABORT => {
|
||||
let display_quiet_abort = callbacks
|
||||
.displayQuietAbort
|
||||
.expect("multi-file quiet-abort callback is required");
|
||||
unsafe { display_quiet_abort(callbacks.opaque) };
|
||||
1
|
||||
}
|
||||
FIO_MULTI_FILES_ACTION_CONFIRM => {
|
||||
let require_confirmation = callbacks
|
||||
.requireConfirmation
|
||||
.expect("multi-file confirmation callback is required");
|
||||
unsafe { require_confirmation(callbacks.opaque, has_stdin_input) }
|
||||
}
|
||||
FIO_MULTI_FILES_ACTION_FATAL_STDOUT_REMOVE | FIO_MULTI_FILES_ACTION_FATAL_TEST_REMOVE => {
|
||||
unreachable!("remove-free multi-file action cannot be fatal")
|
||||
}
|
||||
_ => unreachable!("unknown multi-file concatenation action {action}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Select the warning/prompt action for multiple inputs sharing one output.
|
||||
///
|
||||
/// C retains the user-facing diagnostics, preference mutation, and prompt;
|
||||
@@ -3872,6 +4017,168 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MultiFilesConcatTestState {
|
||||
events: Vec<&'static str>,
|
||||
warning_stdout: c_int,
|
||||
warning_name: Vec<u8>,
|
||||
confirmation_has_stdin: c_int,
|
||||
confirmation_result: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_fatal_stdout_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("fatal-stdout");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_fatal_test_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("fatal-test");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_warning_test(
|
||||
context: *mut c_void,
|
||||
has_stdout_output: c_int,
|
||||
out_file_name: *const c_char,
|
||||
) {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("warning");
|
||||
state.warning_stdout = has_stdout_output;
|
||||
state.warning_name = unsafe { CStr::from_ptr(out_file_name) }.to_bytes().to_vec();
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_disable_remove_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("disable-remove");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_quiet_abort_test(context: *mut c_void) {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("quiet-abort");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn multi_files_concat_confirmation_test(
|
||||
context: *mut c_void,
|
||||
has_stdin_input: c_int,
|
||||
) -> c_int {
|
||||
let state = unsafe { &mut *context.cast::<MultiFilesConcatTestState>() };
|
||||
state.events.push("confirmation");
|
||||
state.confirmation_has_stdin = has_stdin_input;
|
||||
state.confirmation_result
|
||||
}
|
||||
|
||||
fn multi_files_concat_callbacks(
|
||||
state: &mut MultiFilesConcatTestState,
|
||||
) -> FIO_multiFilesConcatCallbacks_t {
|
||||
FIO_multiFilesConcatCallbacks_t {
|
||||
opaque: (state as *mut MultiFilesConcatTestState).cast(),
|
||||
fatalStdoutRemove: Some(multi_files_concat_fatal_stdout_test),
|
||||
fatalTestRemove: Some(multi_files_concat_fatal_test_test),
|
||||
displayWarning: Some(multi_files_concat_warning_test),
|
||||
disableRemove: Some(multi_files_concat_disable_remove_test),
|
||||
displayQuietAbort: Some(multi_files_concat_quiet_abort_test),
|
||||
requireConfirmation: Some(multi_files_concat_confirmation_test),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_files_concat_wrapper_preserves_warning_disable_recheck_order() {
|
||||
let output = CString::new("output.zst").unwrap();
|
||||
let mut state = MultiFilesConcatTestState {
|
||||
confirmation_result: 0,
|
||||
..MultiFilesConcatTestState::default()
|
||||
};
|
||||
let callbacks = multi_files_concat_callbacks(&mut state);
|
||||
|
||||
let result = unsafe {
|
||||
FIO_rust_multiFilesConcatWarning(3, 0, 0, 1, 1, 0, 2, 1, 1, output.as_ptr(), &callbacks)
|
||||
};
|
||||
|
||||
assert_eq!(result, 0);
|
||||
assert_eq!(state.events, ["warning", "disable-remove", "confirmation"]);
|
||||
assert_eq!(state.warning_stdout, 0);
|
||||
assert_eq!(state.warning_name, b"output.zst");
|
||||
assert_eq!(state.confirmation_has_stdin, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_files_concat_wrapper_keeps_fatal_and_quiet_callbacks_on_their_paths() {
|
||||
let output = CString::new("output.zst").unwrap();
|
||||
|
||||
let mut state = MultiFilesConcatTestState::default();
|
||||
let callbacks = multi_files_concat_callbacks(&mut state);
|
||||
let result = unsafe {
|
||||
FIO_rust_multiFilesConcatWarning(3, 1, 0, 1, 1, 0, 2, 1, 0, output.as_ptr(), &callbacks)
|
||||
};
|
||||
assert_eq!(result, 1);
|
||||
assert_eq!(state.events, ["fatal-stdout"]);
|
||||
|
||||
let mut state = MultiFilesConcatTestState::default();
|
||||
let callbacks = multi_files_concat_callbacks(&mut state);
|
||||
let result = unsafe {
|
||||
FIO_rust_multiFilesConcatWarning(3, 0, 1, 1, 1, 0, 2, 1, 0, output.as_ptr(), &callbacks)
|
||||
};
|
||||
assert_eq!(result, 1);
|
||||
assert_eq!(state.events, ["fatal-test"]);
|
||||
|
||||
let mut state = MultiFilesConcatTestState::default();
|
||||
let callbacks = multi_files_concat_callbacks(&mut state);
|
||||
let result = unsafe {
|
||||
FIO_rust_multiFilesConcatWarning(3, 0, 0, 1, 0, 0, 1, 1, 0, output.as_ptr(), &callbacks)
|
||||
};
|
||||
assert_eq!(result, 1);
|
||||
assert_eq!(state.events, ["warning", "quiet-abort"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_files_concat_callback_bridge_matches_c_layout() {
|
||||
let word = size_of::<*mut c_void>();
|
||||
let callback = size_of::<Option<FIO_multiFilesConcatFatalFn>>();
|
||||
|
||||
assert_eq!(size_of::<Option<FIO_multiFilesConcatWarningFn>>(), callback);
|
||||
assert_eq!(
|
||||
size_of::<Option<FIO_multiFilesConcatDisableRemoveFn>>(),
|
||||
callback
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<Option<FIO_multiFilesConcatQuietAbortFn>>(),
|
||||
callback
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<Option<FIO_multiFilesConcatConfirmationFn>>(),
|
||||
callback
|
||||
);
|
||||
assert_eq!(offset_of!(FIO_multiFilesConcatCallbacks_t, opaque), 0);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, fatalStdoutRemove),
|
||||
word
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, fatalTestRemove),
|
||||
word + callback
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, displayWarning),
|
||||
word + 2 * callback
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, disableRemove),
|
||||
word + 3 * callback
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, displayQuietAbort),
|
||||
word + 4 * callback
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_multiFilesConcatCallbacks_t, requireConfirmation),
|
||||
word + 5 * callback
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<FIO_multiFilesConcatCallbacks_t>(),
|
||||
word + 6 * callback
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_shims_preserve_fields_and_c_layout() {
|
||||
let input_word = size_of::<*const c_void>();
|
||||
|
||||
Reference in New Issue
Block a user