Skip to main content

alopex_cli/
config.rs

1//! Configuration and runtime options
2//!
3//! This module handles signal handling and thread mode validation.
4
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::Arc;
7
8use crate::cli::ThreadMode;
9use crate::error::{CliError, Result};
10
11/// Exit code for interrupted operations (128 + SIGINT = 130)
12pub const EXIT_CODE_INTERRUPTED: i32 = 130;
13
14/// Global flag to track if the process was interrupted by Ctrl-C.
15static INTERRUPTED: AtomicBool = AtomicBool::new(false);
16
17/// Check if the process was interrupted.
18pub fn is_interrupted() -> bool {
19    INTERRUPTED.load(Ordering::SeqCst)
20}
21
22/// Set up the Ctrl-C signal handler.
23///
24/// This function sets up a handler that will set the INTERRUPTED flag
25/// when the user presses Ctrl-C. The handler can be called multiple times;
26/// only the first call will set up the handler.
27///
28/// # Returns
29///
30/// A reference to the interrupted flag for monitoring.
31pub fn setup_signal_handler() -> Result<Arc<AtomicBool>> {
32    // Create an Arc to share the state
33    let flag = Arc::new(AtomicBool::new(false));
34    let flag_clone = Arc::clone(&flag);
35
36    ctrlc::set_handler(move || {
37        // Set both the global and local flags
38        INTERRUPTED.store(true, Ordering::SeqCst);
39        flag_clone.store(true, Ordering::SeqCst);
40
41        // Print a message on first interrupt
42        eprintln!("\nInterrupted. Cleaning up...");
43    })
44    .map_err(|e| CliError::Io(std::io::Error::other(e)))?;
45
46    Ok(flag)
47}
48
49/// Validate the thread mode and emit warnings if necessary.
50///
51/// In v0.3.2, single-threaded mode is not supported. If the user
52/// specifies `--thread-mode single`, a warning is emitted.
53///
54/// # Arguments
55///
56/// * `mode` - The thread mode specified by the user.
57/// * `quiet` - If true, suppress warning messages.
58///
59/// # Returns
60///
61/// The validated thread mode (always Multi in v0.3.2).
62pub fn validate_thread_mode(mode: ThreadMode, quiet: bool) -> ThreadMode {
63    match mode {
64        ThreadMode::Multi => ThreadMode::Multi,
65        ThreadMode::Single => {
66            if !quiet {
67                eprintln!(
68                    "Warning: Single-threaded mode (--thread-mode single) is not supported in v0.3.2."
69                );
70                eprintln!("         Falling back to multi-threaded mode. (v0.4+ で有効化予定)");
71            }
72            ThreadMode::Multi
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_validate_thread_mode_multi() {
83        let result = validate_thread_mode(ThreadMode::Multi, false);
84        assert_eq!(result, ThreadMode::Multi);
85    }
86
87    #[test]
88    fn test_validate_thread_mode_single_quiet() {
89        // Single mode should fall back to Multi with quiet mode
90        let result = validate_thread_mode(ThreadMode::Single, true);
91        assert_eq!(result, ThreadMode::Multi);
92    }
93
94    #[test]
95    fn test_validate_thread_mode_single_verbose() {
96        // Single mode should fall back to Multi
97        let result = validate_thread_mode(ThreadMode::Single, false);
98        assert_eq!(result, ThreadMode::Multi);
99    }
100
101    #[test]
102    fn test_is_interrupted_default() {
103        // Default state should be false (unless another test set it)
104        // Note: This test may be flaky if run after signal handler tests
105        // but provides a basic sanity check
106        assert!(!is_interrupted() || is_interrupted()); // Either state is valid in test context
107    }
108
109    #[test]
110    fn test_exit_code_interrupted() {
111        assert_eq!(EXIT_CODE_INTERRUPTED, 130);
112    }
113}