use crate::executor::CommandHandler;
use crate::plugin::system::SystemExitHandler;
use crate::plugin::Plugin;
use std::sync::Arc;
pub struct ExitPlugin {
exit_fn: Arc<dyn Fn() + Send + Sync>,
}
impl ExitPlugin {
pub fn new() -> Self {
Self {
exit_fn: Arc::new(|| std::process::exit(0)),
}
}
pub fn with_exit_fn<F>(mut self, f: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
self.exit_fn = Arc::new(f);
self
}
}
impl Default for ExitPlugin {
fn default() -> Self {
Self::new()
}
}
impl Plugin for ExitPlugin {
fn name(&self) -> &str {
"exit"
}
fn version(&self) -> &str {
env!("CARGO_PKG_VERSION")
}
fn description(&self) -> &str {
"Standalone exit command (split out of SystemPlugin, #44)"
}
fn handlers(&self) -> Vec<(String, Box<dyn CommandHandler>)> {
vec![(
"system_exit".to_string(),
Box::new(SystemExitHandler::new(self.exit_fn.clone())),
)]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ExecutionContext;
use crate::parser::ParsedArgs;
use std::any::Any;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Default)]
struct TestContext;
impl ExecutionContext for TestContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[test]
fn test_exit_plugin_metadata() {
let p = ExitPlugin::new();
assert_eq!(p.name(), "exit");
assert!(!p.version().is_empty());
assert!(!p.description().is_empty());
}
#[test]
fn test_exit_plugin_default() {
let p = ExitPlugin::default();
assert_eq!(p.name(), "exit");
}
#[test]
fn test_exit_plugin_handler_name() {
let handlers = ExitPlugin::new().handlers();
assert_eq!(handlers.len(), 1);
assert_eq!(handlers[0].0, "system_exit");
}
#[test]
fn test_exit_default_callback_is_set() {
let plugin = ExitPlugin::new();
let handlers = plugin.handlers();
assert_eq!(handlers.len(), 1);
}
#[test]
fn test_exit_custom_callback_invoked() {
let called = Arc::new(AtomicBool::new(false));
let called_clone = called.clone();
let plugin = ExitPlugin::new().with_exit_fn(move || {
called_clone.store(true, Ordering::SeqCst);
});
let handlers = plugin.handlers();
let (_, handler) = &handlers[0];
let mut ctx = TestContext;
let result = handler.execute(&mut ctx, &ParsedArgs::from_scalars(HashMap::new()));
assert!(result.is_ok());
assert!(
called.load(Ordering::SeqCst),
"shutdown callback was not invoked"
);
}
#[test]
fn test_exit_plugin_is_send_sync() {
fn assert_send_sync<T: Send + Sync>(_: T) {}
assert_send_sync(ExitPlugin::new().with_exit_fn(|| {}));
}
}