Module context

Module context 

Source
Expand description

Execution context module

This module provides traits and utilities for managing execution context in CLI/REPL applications built with dynamic-cli.

§Overview

The execution context is shared state that persists across command executions. Each application defines its own context type that implements the ExecutionContext trait.

§Key Concepts

§Execution Context

The context holds application-specific state that commands can read and modify:

use dynamic_cli::context::ExecutionContext;
use std::any::Any;

#[derive(Default)]
struct AppContext {
    session_id: String,
    user_data: Vec<String>,
    settings: std::collections::HashMap<String, String>,
}

impl ExecutionContext for AppContext {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}

§Type-Safe Downcasting

Since the framework works with trait objects, commands must downcast the context to access their specific type:

use dynamic_cli::context::{ExecutionContext, downcast_mut};

fn my_command(context: &mut dyn ExecutionContext) -> Result<(), String> {
    // Downcast to concrete type
    let app_ctx = downcast_mut::<AppContext>(context)
        .ok_or("Invalid context type")?;

    // Use the context
    app_ctx.counter += 1;

    Ok(())
}

§Thread Safety

All contexts must be Send + Sync to support:

  • Multi-threaded command execution
  • Async/await patterns
  • Future framework extensibility

§Common Patterns

§Stateless Context

For simple applications that don’t need state:

use dynamic_cli::context::ExecutionContext;
use std::any::Any;

#[derive(Default)]
struct EmptyContext;

impl ExecutionContext for EmptyContext {
    fn as_any(&self) -> &dyn Any { self }
    fn as_any_mut(&mut self) -> &mut dyn Any { self }
}

§Stateful Context

For applications that maintain state:

use dynamic_cli::context::ExecutionContext;
use std::any::Any;
use std::collections::HashMap;

struct DatabaseContext {
    connection_pool: Vec<String>, // Simplified example
    cache: HashMap<String, String>,
    transaction_count: u64,
}

impl Default for DatabaseContext {
    fn default() -> Self {
        Self {
            connection_pool: vec!["conn1".to_string()],
            cache: HashMap::new(),
            transaction_count: 0,
        }
    }
}

impl ExecutionContext for DatabaseContext {
    fn as_any(&self) -> &dyn Any { self }
    fn as_any_mut(&mut self) -> &mut dyn Any { self }
}

§Error Handling in Commands

Best practice for handling downcast failures:

use dynamic_cli::context::{ExecutionContext, downcast_mut};

fn robust_handler(
    context: &mut dyn ExecutionContext
) -> Result<(), Box<dyn std::error::Error>> {
    let ctx = downcast_mut::<MyContext>(context)
        .ok_or("Context type mismatch: expected MyContext")?;

    ctx.value += 1;
    Ok(())
}

§Architecture Notes

§Why Use Trait Objects?

The framework uses dyn ExecutionContext because:

  1. Each application defines its own context type
  2. The framework can’t know concrete types at compile time
  3. This provides maximum flexibility for users

§Why Require Send + Sync?

Thread safety bounds enable:

  • Sharing contexts across threads
  • Compatibility with async runtimes (tokio, async-std)
  • Future features like parallel command execution

§Performance Considerations

  • Downcasting has minimal overhead (type ID comparison)
  • Context access is not on the hot path for most commands
  • The trait object indirection is negligible compared to I/O operations

§See Also

Re-exports§

pub use traits::downcast_mut;
pub use traits::downcast_ref;
pub use traits::ExecutionContext;

Modules§

traits
Execution context traits