use std::any::Any;
use std::collections::HashMap;
pub struct PluginContext {
data: HashMap<String, HashMap<String, Box<dyn Any + Send>>>,
current_plugin: Option<String>,
terminal_size: (u16, u16),
is_running: bool,
}
impl PluginContext {
pub fn new() -> Self {
Self {
data: HashMap::new(),
current_plugin: None,
terminal_size: (80, 24),
is_running: false,
}
}
pub(crate) fn set_current_plugin(&mut self, name: &str) {
self.current_plugin = Some(name.to_string());
}
pub(crate) fn clear_current_plugin(&mut self) {
self.current_plugin = None;
}
pub(crate) fn set_terminal_size(&mut self, width: u16, height: u16) {
self.terminal_size = (width, height);
}
pub(crate) fn set_running(&mut self, running: bool) {
self.is_running = running;
}
pub fn terminal_size(&self) -> (u16, u16) {
self.terminal_size
}
pub fn is_running(&self) -> bool {
self.is_running
}
pub fn set_data<T: Any + Send>(&mut self, key: &str, value: T) {
let plugin_name = self.current_plugin.clone().unwrap_or_default();
let plugin_data = self.data.entry(plugin_name).or_default();
plugin_data.insert(key.to_string(), Box::new(value));
}
pub fn get_data<T: Any + Send>(&self, key: &str) -> Option<&T> {
let plugin_name = self.current_plugin.as_ref()?;
self.data.get(plugin_name)?.get(key)?.downcast_ref::<T>()
}
pub fn get_data_mut<T: Any + Send>(&mut self, key: &str) -> Option<&mut T> {
let plugin_name = self.current_plugin.clone()?;
self.data
.get_mut(&plugin_name)?
.get_mut(key)?
.downcast_mut::<T>()
}
pub fn remove_data(&mut self, key: &str) -> bool {
if let Some(plugin_name) = &self.current_plugin {
if let Some(plugin_data) = self.data.get_mut(plugin_name) {
return plugin_data.remove(key).is_some();
}
}
false
}
pub fn get_plugin_data<T: Any + Send>(&self, plugin_name: &str, key: &str) -> Option<&T> {
self.data.get(plugin_name)?.get(key)?.downcast_ref::<T>()
}
pub fn log(&self, message: &str) {
if let Some(plugin_name) = &self.current_plugin {
crate::log_debug!("[{}] {}", plugin_name, message);
} else {
crate::log_debug!("{}", message);
}
}
pub fn warn(&self, message: &str) {
if let Some(plugin_name) = &self.current_plugin {
crate::log_warn!("[{}] {}", plugin_name, message);
} else {
crate::log_warn!("{}", message);
}
}
pub fn error(&self, message: &str) {
if let Some(plugin_name) = &self.current_plugin {
crate::log_error!("[{}] {}", plugin_name, message);
} else {
crate::log_error!("{}", message);
}
}
}
impl Default for PluginContext {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_plugin_context_data() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test-plugin");
ctx.set_data("counter", 42i32);
assert_eq!(ctx.get_data::<i32>("counter"), Some(&42));
if let Some(counter) = ctx.get_data_mut::<i32>("counter") {
*counter += 1;
}
assert_eq!(ctx.get_data::<i32>("counter"), Some(&43));
}
#[test]
fn test_plugin_context_namespacing() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("plugin-a");
ctx.set_data("value", "from-a".to_string());
ctx.set_current_plugin("plugin-b");
ctx.set_data("value", "from-b".to_string());
ctx.set_current_plugin("plugin-a");
assert_eq!(ctx.get_data::<String>("value"), Some(&"from-a".to_string()));
ctx.set_current_plugin("plugin-b");
assert_eq!(ctx.get_data::<String>("value"), Some(&"from-b".to_string()));
assert_eq!(
ctx.get_plugin_data::<String>("plugin-a", "value"),
Some(&"from-a".to_string())
);
}
#[test]
fn test_terminal_size() {
let mut ctx = PluginContext::new();
assert_eq!(ctx.terminal_size(), (80, 24));
ctx.set_terminal_size(120, 40);
assert_eq!(ctx.terminal_size(), (120, 40));
}
#[test]
fn test_plugin_context_new() {
let ctx = PluginContext::new();
assert_eq!(ctx.terminal_size(), (80, 24));
assert!(!ctx.is_running());
}
#[test]
fn test_plugin_context_default() {
let ctx = PluginContext::default();
assert_eq!(ctx.terminal_size(), (80, 24));
assert!(!ctx.is_running());
}
#[test]
fn test_is_running() {
let mut ctx = PluginContext::new();
assert!(!ctx.is_running());
ctx.set_running(true);
assert!(ctx.is_running());
ctx.set_running(false);
assert!(!ctx.is_running());
}
#[test]
fn test_clear_current_plugin() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("key", 42i32);
assert_eq!(ctx.get_data::<i32>("key"), Some(&42));
ctx.clear_current_plugin();
assert_eq!(ctx.get_data::<i32>("key"), None);
}
#[test]
fn test_remove_data() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("key", 42i32);
assert_eq!(ctx.get_data::<i32>("key"), Some(&42));
let removed = ctx.remove_data("key");
assert!(removed);
assert_eq!(ctx.get_data::<i32>("key"), None);
}
#[test]
fn test_remove_data_nonexistent() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
let removed = ctx.remove_data("nonexistent");
assert!(!removed);
}
#[test]
fn test_remove_data_no_plugin() {
let mut ctx = PluginContext::new();
let removed = ctx.remove_data("key");
assert!(!removed);
}
#[test]
fn test_get_plugin_data_nonexistent_plugin() {
let ctx = PluginContext::new();
assert_eq!(ctx.get_plugin_data::<i32>("nonexistent", "key"), None);
}
#[test]
fn test_get_plugin_data_nonexistent_key() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("key", 42i32);
assert_eq!(ctx.get_plugin_data::<i32>("test", "nonexistent"), None);
}
#[test]
fn test_get_data_no_plugin() {
let ctx = PluginContext::new();
assert_eq!(ctx.get_data::<i32>("key"), None);
}
#[test]
fn test_get_data_mut_no_plugin() {
let mut ctx = PluginContext::new();
assert_eq!(ctx.get_data_mut::<i32>("key"), None);
}
#[test]
fn test_get_data_mut_wrong_type() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("key", 42i32);
assert_eq!(ctx.get_data_mut::<String>("key"), None);
}
#[test]
fn test_set_data_without_plugin() {
let mut ctx = PluginContext::new();
ctx.set_data("key", 42i32);
ctx.set_current_plugin("");
assert_eq!(ctx.get_data::<i32>("key"), Some(&42));
}
#[test]
fn test_log_with_plugin() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.log("test message");
}
#[test]
fn test_log_without_plugin() {
let ctx = PluginContext::new();
ctx.log("test message");
}
#[test]
fn test_warn_with_plugin() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.warn("warning message");
}
#[test]
fn test_warn_without_plugin() {
let ctx = PluginContext::new();
ctx.warn("warning message");
}
#[test]
fn test_error_with_plugin() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.error("error message");
}
#[test]
fn test_error_without_plugin() {
let ctx = PluginContext::new();
ctx.error("error message");
}
#[test]
fn test_multiple_data_types() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("int", 42i32);
ctx.set_data("float", 3.14f64);
ctx.set_data("string", "hello".to_string());
ctx.set_data("bool", true);
assert_eq!(ctx.get_data::<i32>("int"), Some(&42));
assert_eq!(ctx.get_data::<f64>("float"), Some(&3.14));
assert_eq!(ctx.get_data::<String>("string"), Some(&"hello".to_string()));
assert_eq!(ctx.get_data::<bool>("bool"), Some(&true));
}
#[test]
fn test_overwrite_data() {
let mut ctx = PluginContext::new();
ctx.set_current_plugin("test");
ctx.set_data("key", 42i32);
assert_eq!(ctx.get_data::<i32>("key"), Some(&42));
ctx.set_data("key", 100i32);
assert_eq!(ctx.get_data::<i32>("key"), Some(&100));
}
}