pub mod traits;
pub use traits::{downcast_mut, downcast_ref, ExecutionContext};
#[cfg(test)]
mod tests {
use super::{downcast_mut, downcast_ref, ExecutionContext};
use std::any::Any;
use std::collections::HashMap;
#[derive(Default)]
struct TestAppContext {
command_count: u32,
last_command: Option<String>,
data: HashMap<String, String>,
}
impl ExecutionContext for TestAppContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Default)]
struct AnotherContext {
value: i32,
}
impl ExecutionContext for AnotherContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[test]
fn test_module_exports() {
let ctx = TestAppContext::default();
let _ctx_ref: &dyn ExecutionContext = &ctx;
let ctx_ref: &dyn ExecutionContext = &ctx;
let _downcasted = downcast_ref::<TestAppContext>(ctx_ref);
}
#[test]
fn test_integration_command_pattern() {
fn increment_command(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
app_ctx.command_count += 1;
app_ctx.last_command = Some("increment".to_string());
Ok(())
}
let mut ctx = TestAppContext::default();
increment_command(&mut ctx).unwrap();
assert_eq!(ctx.command_count, 1);
assert_eq!(ctx.last_command, Some("increment".to_string()));
}
#[test]
fn test_integration_multiple_commands() {
fn command_a(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
app_ctx
.data
.insert("key_a".to_string(), "value_a".to_string());
app_ctx.command_count += 1;
Ok(())
}
fn command_b(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
app_ctx
.data
.insert("key_b".to_string(), "value_b".to_string());
app_ctx.command_count += 1;
Ok(())
}
let mut ctx = TestAppContext::default();
command_a(&mut ctx).unwrap();
command_b(&mut ctx).unwrap();
assert_eq!(ctx.command_count, 2);
assert_eq!(ctx.data.len(), 2);
assert_eq!(ctx.data.get("key_a"), Some(&"value_a".to_string()));
assert_eq!(ctx.data.get("key_b"), Some(&"value_b".to_string()));
}
#[test]
fn test_integration_wrong_context_type() {
fn command_expecting_test_context(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
downcast_mut::<TestAppContext>(ctx).ok_or("Expected TestAppContext")?;
Ok(())
}
let mut wrong_ctx = AnotherContext::default();
let result = command_expecting_test_context(&mut wrong_ctx);
assert!(result.is_err());
}
#[test]
fn test_integration_read_only_access() {
fn read_command_count(ctx: &dyn ExecutionContext) -> Result<u32, String> {
let app_ctx = downcast_ref::<TestAppContext>(ctx).ok_or("Invalid context")?;
Ok(app_ctx.command_count)
}
let mut ctx = TestAppContext::default();
ctx.command_count = 42;
let count = read_command_count(&ctx).unwrap();
assert_eq!(count, 42);
}
#[test]
fn test_integration_stateful_workflow() {
fn init_command(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
app_ctx
.data
.insert("initialized".to_string(), "true".to_string());
app_ctx.last_command = Some("init".to_string());
Ok(())
}
fn process_command(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
if app_ctx.data.get("initialized") != Some(&"true".to_string()) {
return Err("Not initialized".to_string());
}
app_ctx
.data
.insert("processed".to_string(), "true".to_string());
app_ctx.last_command = Some("process".to_string());
Ok(())
}
fn finalize_command(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
if app_ctx.data.get("processed") != Some(&"true".to_string()) {
return Err("Not processed".to_string());
}
app_ctx
.data
.insert("finalized".to_string(), "true".to_string());
app_ctx.last_command = Some("finalize".to_string());
Ok(())
}
let mut ctx = TestAppContext::default();
init_command(&mut ctx).unwrap();
process_command(&mut ctx).unwrap();
finalize_command(&mut ctx).unwrap();
assert_eq!(ctx.data.get("initialized"), Some(&"true".to_string()));
assert_eq!(ctx.data.get("processed"), Some(&"true".to_string()));
assert_eq!(ctx.data.get("finalized"), Some(&"true".to_string()));
assert_eq!(ctx.last_command, Some("finalize".to_string()));
}
#[test]
fn test_integration_error_propagation() {
fn failing_command(ctx: &mut dyn ExecutionContext) -> Result<(), String> {
let _app_ctx = downcast_mut::<TestAppContext>(ctx).ok_or("Invalid context")?;
Err("Command failed".to_string())
}
let mut ctx = TestAppContext::default();
let result = failing_command(&mut ctx);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Command failed");
}
#[test]
fn test_boxed_context() {
let ctx: Box<dyn ExecutionContext> = Box::new(TestAppContext::default());
let downcasted = downcast_ref::<TestAppContext>(&*ctx);
assert!(downcasted.is_some());
}
#[test]
fn test_boxed_context_mutation() {
let mut ctx: Box<dyn ExecutionContext> = Box::new(TestAppContext::default());
if let Some(app_ctx) = downcast_mut::<TestAppContext>(&mut *ctx) {
app_ctx.command_count = 100;
}
let app_ctx = downcast_ref::<TestAppContext>(&*ctx).unwrap();
assert_eq!(app_ctx.command_count, 100);
}
#[derive(Default)]
struct NestedContext {
outer: HashMap<String, InnerContext>,
}
#[derive(Default, Clone)]
struct InnerContext {
values: Vec<i32>,
}
impl ExecutionContext for NestedContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[test]
fn test_nested_context_manipulation() {
fn add_value(ctx: &mut dyn ExecutionContext, key: &str, value: i32) -> Result<(), String> {
let nested = downcast_mut::<NestedContext>(ctx).ok_or("Invalid context")?;
nested
.outer
.entry(key.to_string())
.or_insert_with(InnerContext::default)
.values
.push(value);
Ok(())
}
let mut ctx = NestedContext::default();
add_value(&mut ctx, "group1", 10).unwrap();
add_value(&mut ctx, "group1", 20).unwrap();
add_value(&mut ctx, "group2", 30).unwrap();
assert_eq!(ctx.outer.get("group1").unwrap().values, vec![10, 20]);
assert_eq!(ctx.outer.get("group2").unwrap().values, vec![30]);
}
#[test]
fn test_context_with_lifetime_data() {
#[derive(Default)]
struct RefContext {
owned_data: String,
}
impl ExecutionContext for RefContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
let mut ctx = RefContext {
owned_data: "test".to_string(),
};
let ctx_ref: &mut dyn ExecutionContext = &mut ctx;
if let Some(ref_ctx) = downcast_mut::<RefContext>(ctx_ref) {
ref_ctx.owned_data.push_str(" modified");
}
assert_eq!(ctx.owned_data, "test modified");
}
}