Skip to main content

click/
context.rs

1//! Central execution context for click-rs.
2//!
3//! The [`Context`] struct holds state relevant for script execution at every level.
4//! It manages parameter values, parent-child relationships, and provides access to
5//! shared metadata and resources.
6//!
7//! # Thread-Local Context Stack
8//!
9//! Click uses a thread-local stack to provide implicit access to the current context.
10//! Use [`push_context`], [`pop_context`], and [`get_current_context`] to manage this stack.
11//!
12//! # Example
13//!
14//! ```
15//! use click::context::{Context, ContextBuilder, push_context, pop_context, get_current_context};
16//! use std::sync::Arc;
17//!
18//! let ctx = ContextBuilder::new()
19//!     .info_name("myapp")
20//!     .build();
21//! let ctx = Arc::new(ctx);
22//!
23//! push_context(Arc::clone(&ctx));
24//! assert!(get_current_context().is_some());
25//! pop_context();
26//! assert!(get_current_context().is_none());
27//! ```
28
29use std::any::Any;
30use std::cell::RefCell;
31use std::collections::HashMap;
32use std::sync::Arc;
33
34use crate::error::ClickError;
35use crate::source::ParameterSource;
36
37// Thread-local context stack
38thread_local! {
39    static CONTEXT_STACK: RefCell<Vec<Arc<Context>>> = const { RefCell::new(Vec::new()) };
40}
41
42/// Push a context onto the thread-local context stack.
43///
44/// The pushed context becomes the current context accessible via [`get_current_context`].
45///
46/// # Example
47///
48/// ```
49/// use click::context::{Context, ContextBuilder, push_context, get_current_context, pop_context};
50/// use std::sync::Arc;
51///
52/// let ctx = Arc::new(ContextBuilder::new().info_name("myapp").build());
53/// push_context(Arc::clone(&ctx));
54///
55/// let current = get_current_context().unwrap();
56/// assert_eq!(current.info_name(), Some("myapp"));
57///
58/// pop_context();
59/// ```
60pub fn push_context(ctx: Arc<Context>) {
61    CONTEXT_STACK.with(|stack| {
62        stack.borrow_mut().push(ctx);
63    });
64}
65
66/// Pop and return the top context from the thread-local context stack.
67///
68/// Returns `None` if the stack is empty.
69///
70/// # Example
71///
72/// ```
73/// use click::context::{Context, ContextBuilder, push_context, pop_context};
74/// use std::sync::Arc;
75///
76/// let ctx = Arc::new(ContextBuilder::new().build());
77/// push_context(ctx);
78///
79/// let popped = pop_context();
80/// assert!(popped.is_some());
81/// assert!(pop_context().is_none());
82/// ```
83pub fn pop_context() -> Option<Arc<Context>> {
84    CONTEXT_STACK.with(|stack| stack.borrow_mut().pop())
85}
86
87/// Get the current context from the thread-local stack.
88///
89/// Returns `None` if no context is currently active. Use [`push_context`] to
90/// make a context current.
91///
92/// # Example
93///
94/// ```
95/// use click::context::{Context, ContextBuilder, push_context, pop_context, get_current_context};
96/// use std::sync::Arc;
97///
98/// assert!(get_current_context().is_none());
99///
100/// let ctx = Arc::new(ContextBuilder::new().info_name("cli").build());
101/// push_context(ctx);
102///
103/// let current = get_current_context().expect("context should be available");
104/// assert_eq!(current.info_name(), Some("cli"));
105///
106/// pop_context();
107/// ```
108pub fn get_current_context() -> Option<Arc<Context>> {
109    CONTEXT_STACK.with(|stack| stack.borrow().last().cloned())
110}
111
112/// Type alias for shared values that can be stored in context maps.
113/// Uses Arc to allow cloning and sharing across parent-child contexts.
114pub type BoxedValue = Arc<dyn Any + Send + Sync>;
115
116/// The central execution context for click-rs.
117///
118/// The context holds state relevant for script execution at every single level.
119/// It's normally invisible to commands unless they opt-in to getting access to it.
120///
121/// A context manages:
122/// - Parsed parameter values
123/// - Parent context chain (for nested commands)
124/// - Parameter source tracking (CLI, env, default, prompt)
125/// - Resource cleanup via close callbacks
126/// - Shared metadata between nested contexts
127///
128/// # Construction
129///
130/// Use [`ContextBuilder`] to create a new context with custom settings.
131///
132/// # Example
133///
134/// ```
135/// use click::context::{Context, ContextBuilder};
136/// use std::sync::Arc;
137///
138/// let parent = Arc::new(ContextBuilder::new()
139///     .info_name("cli")
140///     .auto_envvar_prefix("MYAPP")
141///     .build());
142///
143/// let child = ContextBuilder::new()
144///     .info_name("subcommand")
145///     .parent(parent)
146///     .build();
147///
148/// assert_eq!(child.command_path(), "cli subcommand");
149/// ```
150pub struct Context {
151    /// The parent context, if any.
152    parent: Option<Arc<Context>>,
153
154    /// The descriptive information name for this invocation.
155    /// For the toplevel script it's usually the name of the script;
156    /// for subcommands, it's the command name.
157    info_name: Option<String>,
158
159    /// Map of parameter names to their parsed values.
160    params: HashMap<String, BoxedValue>,
161
162    /// Leftover arguments that were not consumed by parameters.
163    args: Vec<String>,
164
165    /// User data object that can be passed between commands.
166    obj: Option<BoxedValue>,
167
168    /// Shared metadata dictionary accessible to all nested contexts.
169    /// Keys should be unique dotted strings (e.g., module paths).
170    meta: HashMap<String, BoxedValue>,
171
172    /// A dictionary with default overrides for parameters.
173    default_map: Option<HashMap<String, BoxedValue>>,
174
175    /// This flag indicates if a subcommand is going to be executed.
176    /// `None` means no subcommand; `Some("*")` for chained commands;
177    /// otherwise the name of the subcommand to execute.
178    invoked_subcommand: Option<String>,
179
180    /// The width of the terminal (None for autodetection).
181    terminal_width: Option<usize>,
182
183    /// The maximum width for content rendered by Click (e.g., help pages).
184    /// Defaults to 80 characters if not overridden.
185    max_content_width: Option<usize>,
186
187    /// If true, extra arguments at the end will not raise an error.
188    allow_extra_args: bool,
189
190    /// If false, options and arguments cannot be mixed.
191    allow_interspersed_args: bool,
192
193    /// If true, unknown options are ignored and kept for later processing.
194    ignore_unknown_options: bool,
195
196    /// The names for the help options. Default is `["--help"]`.
197    help_option_names: Vec<String>,
198
199    /// If true, Click will parse without interactivity or callback invocation.
200    /// Default values will also be ignored. Useful for shell completion.
201    resilient_parsing: bool,
202
203    /// The prefix to use for automatic environment variables.
204    /// If `None`, reading from environment variables is disabled.
205    auto_envvar_prefix: Option<String>,
206
207    /// Controls if styling output is wanted or not.
208    color: Option<bool>,
209
210    /// Show option default values when formatting help text.
211    show_default: Option<bool>,
212
213    /// Map of parameter names to their value sources.
214    parameter_source: HashMap<String, ParameterSource>,
215
216    /// Callbacks to run when the context is closed.
217    /// These are stored in a RefCell to allow mutation even with shared references.
218    close_callbacks: RefCell<Vec<Box<dyn FnOnce() + Send>>>,
219}
220
221// Manual Debug implementation since close_callbacks contains non-Debug closures
222impl std::fmt::Debug for Context {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        f.debug_struct("Context")
225            .field(
226                "parent",
227                &self.parent.as_ref().map(|p| p.info_name.as_deref()),
228            )
229            .field("info_name", &self.info_name)
230            .field("params", &format!("<{} params>", self.params.len()))
231            .field("args", &self.args)
232            .field("obj", &self.obj.as_ref().map(|_| "<obj>"))
233            .field("meta", &format!("<{} entries>", self.meta.len()))
234            .field(
235                "default_map",
236                &self
237                    .default_map
238                    .as_ref()
239                    .map(|m| format!("<{} entries>", m.len())),
240            )
241            .field("invoked_subcommand", &self.invoked_subcommand)
242            .field("terminal_width", &self.terminal_width)
243            .field("max_content_width", &self.max_content_width)
244            .field("allow_extra_args", &self.allow_extra_args)
245            .field("allow_interspersed_args", &self.allow_interspersed_args)
246            .field("ignore_unknown_options", &self.ignore_unknown_options)
247            .field("help_option_names", &self.help_option_names)
248            .field("resilient_parsing", &self.resilient_parsing)
249            .field("auto_envvar_prefix", &self.auto_envvar_prefix)
250            .field("color", &self.color)
251            .field("show_default", &self.show_default)
252            .field("parameter_source", &self.parameter_source)
253            .field(
254                "close_callbacks",
255                &format!("<{} callbacks>", self.close_callbacks.borrow().len()),
256            )
257            .finish()
258    }
259}
260
261// Manual Default implementation since we can't derive it due to the close_callbacks field
262impl Default for Context {
263    fn default() -> Self {
264        Self {
265            parent: None,
266            info_name: None,
267            params: HashMap::new(),
268            args: Vec::new(),
269            obj: None,
270            meta: HashMap::new(),
271            default_map: None,
272            invoked_subcommand: None,
273            terminal_width: None,
274            max_content_width: None,
275            allow_extra_args: false,
276            allow_interspersed_args: true,
277            ignore_unknown_options: false,
278            help_option_names: vec!["--help".to_string()],
279            resilient_parsing: false,
280            auto_envvar_prefix: None,
281            color: None,
282            show_default: None,
283            parameter_source: HashMap::new(),
284            close_callbacks: RefCell::new(Vec::new()),
285        }
286    }
287}
288
289impl Context {
290    /// Create a new context with default settings.
291    ///
292    /// For more control over the context's configuration, use [`ContextBuilder`].
293    #[inline]
294    pub fn new() -> Self {
295        Self::default()
296    }
297
298    /// Get the parent context, if any.
299    #[inline]
300    pub fn parent(&self) -> Option<&Arc<Context>> {
301        self.parent.as_ref()
302    }
303
304    /// Get the info name for this context.
305    #[inline]
306    pub fn info_name(&self) -> Option<&str> {
307        self.info_name.as_deref()
308    }
309
310    /// Get the parsed parameter values.
311    #[inline]
312    pub fn params(&self) -> &HashMap<String, BoxedValue> {
313        &self.params
314    }
315
316    /// Get a mutable reference to the parsed parameter values.
317    #[inline]
318    pub fn params_mut(&mut self) -> &mut HashMap<String, BoxedValue> {
319        &mut self.params
320    }
321
322    /// Get a parameter value by name, downcast to the expected type.
323    ///
324    /// Returns `None` if the parameter doesn't exist or has the wrong type.
325    ///
326    /// # Example
327    ///
328    /// ```
329    /// use click::context::ContextBuilder;
330    /// use std::sync::Arc;
331    ///
332    /// let mut ctx = ContextBuilder::new().build();
333    /// ctx.params_mut().insert("count".to_string(), Arc::new(42i32));
334    ///
335    /// let count: Option<&i32> = ctx.get_param("count");
336    /// assert_eq!(count, Some(&42));
337    /// ```
338    pub fn get_param<T: 'static>(&self, name: &str) -> Option<&T> {
339        self.params.get(name).and_then(|v| v.downcast_ref::<T>())
340    }
341
342    /// Get the leftover arguments.
343    #[inline]
344    pub fn args(&self) -> &[String] {
345        &self.args
346    }
347
348    /// Get a mutable reference to the leftover arguments.
349    #[inline]
350    pub fn args_mut(&mut self) -> &mut Vec<String> {
351        &mut self.args
352    }
353
354    /// Get the user object, downcast to the expected type.
355    ///
356    /// # Example
357    ///
358    /// ```
359    /// use click::context::ContextBuilder;
360    ///
361    /// #[derive(Debug)]
362    /// struct AppState { db_url: String }
363    ///
364    /// let ctx = ContextBuilder::new()
365    ///     .obj(AppState { db_url: "postgres://...".to_string() })
366    ///     .build();
367    ///
368    /// let state: Option<&AppState> = ctx.obj();
369    /// assert!(state.is_some());
370    /// ```
371    pub fn obj<T: 'static>(&self) -> Option<&T> {
372        self.obj.as_ref().and_then(|v| v.downcast_ref::<T>())
373    }
374
375    /// Find a user object of type `T` by walking up the parent chain.
376    ///
377    /// This is a convenience for decorator-style helpers that want to accept
378    /// objects stored in a parent context.
379    pub fn find_obj<T: 'static>(&self) -> Option<&T> {
380        let mut current: Option<&Context> = Some(self);
381        while let Some(ctx) = current {
382            if let Some(obj) = ctx.obj::<T>() {
383                return Some(obj);
384            }
385            current = ctx.parent.as_ref().map(|p| p.as_ref());
386        }
387        None
388    }
389
390    /// Set the user object.
391    pub fn set_obj<T: Any + Send + Sync + 'static>(&mut self, obj: T) {
392        self.obj = Some(Arc::new(obj));
393    }
394
395    /// Get the shared metadata map.
396    #[inline]
397    pub fn meta(&self) -> &HashMap<String, BoxedValue> {
398        &self.meta
399    }
400
401    /// Get a mutable reference to the shared metadata map.
402    #[inline]
403    pub fn meta_mut(&mut self) -> &mut HashMap<String, BoxedValue> {
404        &mut self.meta
405    }
406
407    /// Get a metadata value by key, downcast to the expected type.
408    pub fn get_meta<T: 'static>(&self, key: &str) -> Option<&T> {
409        self.meta.get(key).and_then(|v| v.downcast_ref::<T>())
410    }
411
412    /// Get the invoked subcommand name, if any.
413    #[inline]
414    pub fn invoked_subcommand(&self) -> Option<&str> {
415        self.invoked_subcommand.as_deref()
416    }
417
418    /// Set the invoked subcommand name.
419    #[inline]
420    pub fn set_invoked_subcommand(&mut self, name: Option<String>) {
421        self.invoked_subcommand = name;
422    }
423
424    /// Get the terminal width.
425    #[inline]
426    pub fn terminal_width(&self) -> Option<usize> {
427        self.terminal_width
428    }
429
430    /// Get the maximum content width.
431    #[inline]
432    pub fn max_content_width(&self) -> Option<usize> {
433        self.max_content_width
434    }
435
436    /// Check if extra arguments are allowed.
437    #[inline]
438    pub fn allow_extra_args(&self) -> bool {
439        self.allow_extra_args
440    }
441
442    /// Check if interspersed arguments are allowed.
443    #[inline]
444    pub fn allow_interspersed_args(&self) -> bool {
445        self.allow_interspersed_args
446    }
447
448    /// Check if unknown options should be ignored.
449    #[inline]
450    pub fn ignore_unknown_options(&self) -> bool {
451        self.ignore_unknown_options
452    }
453
454    /// Get the help option names.
455    #[inline]
456    pub fn help_option_names(&self) -> &[String] {
457        &self.help_option_names
458    }
459
460    /// Check if resilient parsing is enabled.
461    #[inline]
462    pub fn resilient_parsing(&self) -> bool {
463        self.resilient_parsing
464    }
465
466    /// Get the auto envvar prefix.
467    #[inline]
468    pub fn auto_envvar_prefix(&self) -> Option<&str> {
469        self.auto_envvar_prefix.as_deref()
470    }
471
472    /// Get the color setting.
473    #[inline]
474    pub fn color(&self) -> Option<bool> {
475        self.color
476    }
477
478    /// Get the show_default setting.
479    #[inline]
480    pub fn show_default(&self) -> Option<bool> {
481        self.show_default
482    }
483
484    /// Get the computed command path.
485    ///
486    /// This is used for the `usage` information on the help page.
487    /// It's automatically created by combining the info names of the
488    /// chain of contexts to the root.
489    ///
490    /// # Example
491    ///
492    /// ```
493    /// use click::context::ContextBuilder;
494    /// use std::sync::Arc;
495    ///
496    /// let root = Arc::new(ContextBuilder::new().info_name("cli").build());
497    /// let sub = ContextBuilder::new()
498    ///     .info_name("subcommand")
499    ///     .parent(root)
500    ///     .build();
501    ///
502    /// assert_eq!(sub.command_path(), "cli subcommand");
503    /// ```
504    pub fn command_path(&self) -> String {
505        let mut parts = Vec::new();
506
507        // Walk up the parent chain to collect all info names
508        let mut current: Option<&Context> = Some(self);
509        while let Some(ctx) = current {
510            if let Some(name) = &ctx.info_name {
511                parts.push(name.as_str());
512            }
513            current = ctx.parent.as_ref().map(|p| p.as_ref());
514        }
515
516        // Reverse to get root-to-leaf order
517        parts.reverse();
518        parts.join(" ")
519    }
520
521    /// Find the root context.
522    ///
523    /// Walks up the parent chain to find the outermost context.
524    pub fn find_root(&self) -> &Context {
525        let mut current: &Context = self;
526        while let Some(parent) = &current.parent {
527            current = parent.as_ref();
528        }
529        current
530    }
531
532    /// Get the source of a parameter's value.
533    ///
534    /// Returns `None` if the parameter was not provided from any source.
535    ///
536    /// # Example
537    ///
538    /// ```
539    /// use click::context::ContextBuilder;
540    /// use click::ParameterSource;
541    ///
542    /// let mut ctx = ContextBuilder::new().build();
543    /// ctx.set_parameter_source("name", ParameterSource::CommandLine);
544    ///
545    /// assert_eq!(ctx.get_parameter_source("name"), Some(ParameterSource::CommandLine));
546    /// assert_eq!(ctx.get_parameter_source("other"), None);
547    /// ```
548    #[inline]
549    pub fn get_parameter_source(&self, name: &str) -> Option<ParameterSource> {
550        self.parameter_source.get(name).copied()
551    }
552
553    /// Set the source of a parameter's value.
554    ///
555    /// This indicates the location from which the value of the parameter was obtained.
556    #[inline]
557    pub fn set_parameter_source(&mut self, name: &str, source: ParameterSource) {
558        self.parameter_source.insert(name.to_string(), source);
559    }
560
561    /// Create a usage error for this context.
562    ///
563    /// Returns a [`ClickError::UsageError`] with the given message.
564    ///
565    /// # Example
566    ///
567    /// ```
568    /// use click::context::ContextBuilder;
569    /// use click::error::ClickError;
570    ///
571    /// let ctx = ContextBuilder::new().info_name("myapp").build();
572    /// let err = ctx.fail("invalid input");
573    ///
574    /// assert!(matches!(err, ClickError::UsageError { .. }));
575    /// ```
576    pub fn fail(&self, message: &str) -> ClickError {
577        ClickError::usage(message)
578    }
579
580    /// Create an abort error.
581    ///
582    /// Returns a [`ClickError::Abort`].
583    ///
584    /// # Example
585    ///
586    /// ```
587    /// use click::context::ContextBuilder;
588    /// use click::error::ClickError;
589    ///
590    /// let ctx = ContextBuilder::new().build();
591    /// let err = ctx.abort();
592    ///
593    /// assert!(matches!(err, ClickError::Abort));
594    /// ```
595    #[inline]
596    pub fn abort(&self) -> ClickError {
597        ClickError::abort()
598    }
599
600    /// Create an exit error with the given code.
601    ///
602    /// Returns a [`ClickError::Exit`] with the specified exit code.
603    ///
604    /// # Example
605    ///
606    /// ```
607    /// use click::context::ContextBuilder;
608    /// use click::error::ClickError;
609    ///
610    /// let ctx = ContextBuilder::new().build();
611    /// let err = ctx.exit(1);
612    ///
613    /// assert!(matches!(err, ClickError::Exit { code: 1 }));
614    /// ```
615    #[inline]
616    pub fn exit(&self, code: i32) -> ClickError {
617        ClickError::exit(code)
618    }
619
620    /// Register a callback to be called when the context is closed.
621    ///
622    /// This can be used to close resources opened during script execution.
623    ///
624    /// # Example
625    ///
626    /// ```
627    /// use click::context::ContextBuilder;
628    /// use std::sync::atomic::{AtomicBool, Ordering};
629    /// use std::sync::Arc;
630    ///
631    /// let closed = Arc::new(AtomicBool::new(false));
632    /// let closed_clone = Arc::clone(&closed);
633    ///
634    /// let mut ctx = ContextBuilder::new().build();
635    /// ctx.call_on_close(move || {
636    ///     closed_clone.store(true, Ordering::SeqCst);
637    /// });
638    ///
639    /// assert!(!closed.load(Ordering::SeqCst));
640    /// ctx.close();
641    /// assert!(closed.load(Ordering::SeqCst));
642    /// ```
643    pub fn call_on_close(&self, f: impl FnOnce() + Send + 'static) {
644        self.close_callbacks.borrow_mut().push(Box::new(f));
645    }
646
647    /// Invoke all close callbacks.
648    ///
649    /// This runs all functions registered with [`call_on_close`](Self::call_on_close)
650    /// in reverse order (last registered, first called).
651    pub fn close(&self) {
652        let callbacks: Vec<_> = self.close_callbacks.borrow_mut().drain(..).collect();
653        // Run in reverse order (LIFO)
654        for callback in callbacks.into_iter().rev() {
655            callback();
656        }
657    }
658
659    /// Look up a default value for a parameter from the default_map.
660    ///
661    /// Returns `None` if no default_map is set or the parameter is not in the map.
662    ///
663    /// # Example
664    ///
665    /// ```
666    /// use click::context::ContextBuilder;
667    /// use std::collections::HashMap;
668    /// use std::sync::Arc;
669    ///
670    /// let mut defaults: HashMap<String, Arc<dyn std::any::Any + Send + Sync>> = HashMap::new();
671    /// defaults.insert("count".to_string(), Arc::new(10i32));
672    ///
673    /// let ctx = ContextBuilder::new()
674    ///     .default_map(defaults)
675    ///     .build();
676    ///
677    /// let default = ctx.lookup_default("count");
678    /// assert!(default.is_some());
679    /// assert_eq!(default.and_then(|v| v.downcast_ref::<i32>()), Some(&10));
680    /// ```
681    pub fn lookup_default(&self, name: &str) -> Option<&dyn Any> {
682        self.default_map
683            .as_ref()
684            .and_then(|map| map.get(name))
685            .map(|v| v.as_ref() as &dyn Any)
686    }
687
688    /// Look up a default value from the default_map and return the stored Arc.
689    pub fn lookup_default_value(&self, name: &str) -> Option<BoxedValue> {
690        self.default_map
691            .as_ref()
692            .and_then(|map| map.get(name))
693            .cloned()
694    }
695
696    /// Programmatically invoke another command with the given arguments.
697    ///
698    /// This creates a child context for the invoked command and runs it.
699    /// The invoked command will have this context as its parent.
700    ///
701    /// This is useful for calling other commands from within a command callback,
702    /// similar to Python Click's `ctx.invoke()`.
703    ///
704    /// # Arguments
705    ///
706    /// * `cmd` - The command to invoke
707    /// * `args` - The arguments to pass to the command
708    ///
709    /// # Example
710    ///
711    /// ```
712    /// use click::command::Command;
713    /// use click::context::ContextBuilder;
714    /// use std::sync::Arc;
715    /// use std::sync::atomic::{AtomicBool, Ordering};
716    ///
717    /// let invoked = Arc::new(AtomicBool::new(false));
718    /// let invoked_clone = Arc::clone(&invoked);
719    ///
720    /// let other_cmd = Command::new("other")
721    ///     .callback(move |_ctx| {
722    ///         invoked_clone.store(true, Ordering::SeqCst);
723    ///         Ok(())
724    ///     })
725    ///     .build();
726    ///
727    /// let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
728    /// let result = ctx.invoke(&other_cmd, &[]);
729    /// assert!(result.is_ok());
730    /// assert!(invoked.load(Ordering::SeqCst));
731    /// ```
732    pub fn invoke(
733        self: &Arc<Self>,
734        cmd: &dyn crate::group::CommandLike,
735        args: &[String],
736    ) -> Result<(), ClickError> {
737        // Get the command name for the child context
738        let cmd_name = cmd.name().unwrap_or("invoked");
739
740        // Create child context with this context as parent
741        let child_ctx = cmd.make_context(cmd_name, args.to_vec(), Some(Arc::clone(self)))?;
742        let child_ctx = Arc::new(child_ctx);
743
744        // Push child context onto thread-local stack
745        push_context(Arc::clone(&child_ctx));
746
747        // Invoke the command
748        let result = cmd.invoke(&child_ctx);
749
750        // Pop context
751        pop_context();
752
753        // Run close callbacks on child
754        child_ctx.close();
755
756        result
757    }
758
759    /// Forward the current context's parameters to another command.
760    ///
761    /// This is similar to [`invoke`](Self::invoke), but reuses the current context's
762    /// parameter values instead of parsing new arguments. The invoked command will
763    /// see the same parameter values as this context.
764    ///
765    /// This is useful for delegating to another command while preserving the
766    /// current context's state, similar to Python Click's `ctx.forward()`.
767    ///
768    /// # Arguments
769    ///
770    /// * `cmd` - The command to forward to
771    ///
772    /// # Example
773    ///
774    /// ```
775    /// use click::command::Command;
776    /// use click::context::ContextBuilder;
777    /// use click::group::CommandLike;
778    /// use std::sync::Arc;
779    ///
780    /// let other_cmd = Command::new("other")
781    ///     .callback(|ctx| {
782    ///         // This command will see the forwarded params
783    ///         if let Some(name) = ctx.get_param::<String>("name") {
784    ///             println!("Forwarded name: {}", name);
785    ///         }
786    ///         Ok(())
787    ///     })
788    ///     .build();
789    ///
790    /// let mut ctx = ContextBuilder::new().info_name("main").build();
791    /// ctx.params_mut().insert("name".to_string(), Arc::new("Alice".to_string()));
792    /// let ctx = Arc::new(ctx);
793    ///
794    /// let result = ctx.forward(&other_cmd);
795    /// assert!(result.is_ok());
796    /// ```
797    pub fn forward(
798        self: &Arc<Self>,
799        cmd: &dyn crate::group::CommandLike,
800    ) -> Result<(), ClickError> {
801        // Get the command name for the child context
802        let cmd_name = cmd.name().unwrap_or("forwarded");
803
804        // Create a child context builder with this context as parent
805        let child_builder = ContextBuilder::new()
806            .info_name(cmd_name)
807            .parent(Arc::clone(self));
808
809        // If the parent has an obj, it will be inherited via ContextBuilder.
810        // Build the child context
811        let mut child_ctx = child_builder.build();
812
813        // Copy all parameters from this context to the child
814        for (key, value) in self.params.iter() {
815            child_ctx.params.insert(key.clone(), Arc::clone(value));
816        }
817
818        // Copy parameter sources
819        for (key, source) in self.parameter_source.iter() {
820            child_ctx.parameter_source.insert(key.clone(), *source);
821        }
822
823        let child_ctx = Arc::new(child_ctx);
824
825        // Push child context onto thread-local stack
826        push_context(Arc::clone(&child_ctx));
827
828        // Invoke the command
829        let result = cmd.invoke(&child_ctx);
830
831        // Pop context
832        pop_context();
833
834        // Run close callbacks on child
835        child_ctx.close();
836
837        result
838    }
839
840    /// Execute a function with a resource, ensuring cleanup on close.
841    ///
842    /// This is a convenience method that combines resource registration with
843    /// immediate use. The resource is passed to the provided function, and
844    /// a cleanup callback is registered via [`call_on_close`](Self::call_on_close).
845    ///
846    /// This pattern is useful for managing resources like file handles,
847    /// database connections, or other cleanup-requiring objects within
848    /// command execution.
849    ///
850    /// # Type Parameters
851    ///
852    /// * `T` - The resource type (must be `Send + 'static`)
853    /// * `F` - The function to execute with the resource
854    /// * `C` - The cleanup function
855    /// * `R` - The return type of the function
856    ///
857    /// # Arguments
858    ///
859    /// * `resource` - The resource to manage
860    /// * `f` - The function to execute with the resource
861    /// * `cleanup` - The cleanup function to run when the context closes
862    ///
863    /// # Example
864    ///
865    /// ```
866    /// use click::context::ContextBuilder;
867    /// use std::sync::Arc;
868    /// use std::sync::atomic::{AtomicBool, Ordering};
869    ///
870    /// struct Resource {
871    ///     value: i32,
872    /// }
873    ///
874    /// let cleaned_up = Arc::new(AtomicBool::new(false));
875    /// let cleaned_up_clone = Arc::clone(&cleaned_up);
876    ///
877    /// let ctx = ContextBuilder::new().build();
878    ///
879    /// let result = ctx.with_resource(
880    ///     Resource { value: 42 },
881    ///     |res| res.value * 2,
882    ///     move || {
883    ///         cleaned_up_clone.store(true, Ordering::SeqCst);
884    ///     },
885    /// );
886    ///
887    /// assert_eq!(result, 84);
888    /// assert!(!cleaned_up.load(Ordering::SeqCst)); // Not yet cleaned up
889    ///
890    /// ctx.close();
891    /// assert!(cleaned_up.load(Ordering::SeqCst)); // Now cleaned up
892    /// ```
893    pub fn with_resource<T, F, C, R>(&self, resource: T, f: F, cleanup: C) -> R
894    where
895        T: Send + 'static,
896        F: FnOnce(&T) -> R,
897        C: FnOnce() + Send + 'static,
898    {
899        // Register the cleanup callback
900        self.call_on_close(cleanup);
901
902        // Execute the function with the resource
903        f(&resource)
904    }
905}
906
907/// Builder for creating [`Context`] instances with custom settings.
908///
909/// # Example
910///
911/// ```
912/// use click::context::ContextBuilder;
913///
914/// let ctx = ContextBuilder::new()
915///     .info_name("myapp")
916///     .allow_extra_args(true)
917///     .terminal_width(100)
918///     .color(true)
919///     .build();
920///
921/// assert_eq!(ctx.info_name(), Some("myapp"));
922/// assert!(ctx.allow_extra_args());
923/// assert_eq!(ctx.terminal_width(), Some(100));
924/// assert_eq!(ctx.color(), Some(true));
925/// ```
926#[derive(Default)]
927pub struct ContextBuilder {
928    parent: Option<Arc<Context>>,
929    info_name: Option<String>,
930    obj: Option<BoxedValue>,
931    auto_envvar_prefix: Option<String>,
932    default_map: Option<HashMap<String, BoxedValue>>,
933    terminal_width: Option<usize>,
934    max_content_width: Option<usize>,
935    resilient_parsing: bool,
936    allow_extra_args: Option<bool>,
937    allow_interspersed_args: Option<bool>,
938    ignore_unknown_options: Option<bool>,
939    help_option_names: Option<Vec<String>>,
940    color: Option<bool>,
941    show_default: Option<bool>,
942}
943
944impl ContextBuilder {
945    /// Create a new context builder with default settings.
946    #[inline]
947    pub fn new() -> Self {
948        Self::default()
949    }
950
951    /// Set the parent context.
952    ///
953    /// Settings like `terminal_width`, `color`, and `show_default` will be
954    /// inherited from the parent if not explicitly set.
955    pub fn parent(mut self, parent: Arc<Context>) -> Self {
956        self.parent = Some(parent);
957        self
958    }
959
960    /// Set the info name for this context.
961    ///
962    /// This is typically the command name.
963    pub fn info_name(mut self, name: impl Into<String>) -> Self {
964        self.info_name = Some(name.into());
965        self
966    }
967
968    /// Set the user object.
969    ///
970    /// If not set and there's a parent context, the parent's obj will be inherited.
971    pub fn obj<T: Any + Send + Sync + 'static>(mut self, obj: T) -> Self {
972        self.obj = Some(Arc::new(obj));
973        self
974    }
975
976    /// Set the auto envvar prefix.
977    ///
978    /// If set, parameters can automatically read from environment variables
979    /// with the format `{PREFIX}_{PARAM_NAME}`.
980    pub fn auto_envvar_prefix(mut self, prefix: impl Into<String>) -> Self {
981        self.auto_envvar_prefix = Some(prefix.into());
982        self
983    }
984
985    /// Set the default map.
986    ///
987    /// This provides default values for parameters that override the parameter's
988    /// own default.
989    pub fn default_map(mut self, map: HashMap<String, BoxedValue>) -> Self {
990        self.default_map = Some(map);
991        self
992    }
993
994    /// Set the terminal width.
995    pub fn terminal_width(mut self, width: usize) -> Self {
996        self.terminal_width = Some(width);
997        self
998    }
999
1000    /// Set the maximum content width for help text.
1001    pub fn max_content_width(mut self, width: usize) -> Self {
1002        self.max_content_width = Some(width);
1003        self
1004    }
1005
1006    /// Enable or disable resilient parsing mode.
1007    ///
1008    /// In resilient parsing mode, Click will parse without interactivity or
1009    /// callback invocation. This is useful for shell completion.
1010    pub fn resilient_parsing(mut self, enabled: bool) -> Self {
1011        self.resilient_parsing = enabled;
1012        self
1013    }
1014
1015    /// Set whether extra arguments are allowed.
1016    pub fn allow_extra_args(mut self, allow: bool) -> Self {
1017        self.allow_extra_args = Some(allow);
1018        self
1019    }
1020
1021    /// Set whether interspersed arguments are allowed.
1022    pub fn allow_interspersed_args(mut self, allow: bool) -> Self {
1023        self.allow_interspersed_args = Some(allow);
1024        self
1025    }
1026
1027    /// Set whether unknown options should be ignored.
1028    pub fn ignore_unknown_options(mut self, ignore: bool) -> Self {
1029        self.ignore_unknown_options = Some(ignore);
1030        self
1031    }
1032
1033    /// Set the help option names.
1034    ///
1035    /// Default is `["--help"]`.
1036    pub fn help_option_names(mut self, names: Vec<String>) -> Self {
1037        self.help_option_names = Some(names);
1038        self
1039    }
1040
1041    /// Set the color setting.
1042    pub fn color(mut self, color: bool) -> Self {
1043        self.color = Some(color);
1044        self
1045    }
1046
1047    /// Set the show_default setting.
1048    pub fn show_default(mut self, show: bool) -> Self {
1049        self.show_default = Some(show);
1050        self
1051    }
1052
1053    /// Build the context.
1054    pub fn build(self) -> Context {
1055        // Inherit values from parent if not explicitly set
1056        let (terminal_width, max_content_width, color, show_default, help_option_names, obj, meta) =
1057            if let Some(ref parent) = self.parent {
1058                (
1059                    self.terminal_width.or(parent.terminal_width),
1060                    self.max_content_width.or(parent.max_content_width),
1061                    self.color.or(parent.color),
1062                    self.show_default.or(parent.show_default),
1063                    self.help_option_names
1064                        .unwrap_or_else(|| parent.help_option_names.clone()),
1065                    // Inherit obj from parent if not explicitly set
1066                    self.obj.or_else(|| parent.obj.clone()),
1067                    // Clone meta from parent (child starts with parent's metadata)
1068                    parent.meta.clone(),
1069                )
1070            } else {
1071                (
1072                    self.terminal_width,
1073                    self.max_content_width,
1074                    self.color,
1075                    self.show_default,
1076                    self.help_option_names
1077                        .unwrap_or_else(|| vec!["--help".to_string()]),
1078                    self.obj,
1079                    HashMap::new(),
1080                )
1081            };
1082
1083        // Compute auto_envvar_prefix
1084        let auto_envvar_prefix = if let Some(prefix) = self.auto_envvar_prefix {
1085            // Normalize: uppercase and replace dashes with underscores
1086            Some(prefix.to_uppercase().replace('-', "_"))
1087        } else if let (Some(ref parent), Some(ref info_name)) = (&self.parent, &self.info_name) {
1088            // Expand from parent's prefix
1089            parent.auto_envvar_prefix.as_ref().map(|parent_prefix| {
1090                format!(
1091                    "{}_{}",
1092                    parent_prefix,
1093                    info_name.to_uppercase().replace('-', "_")
1094                )
1095            })
1096        } else {
1097            None
1098        };
1099
1100        // Inherit default_map from parent, with child overrides.
1101        let parent_default_map = self.parent.as_ref().and_then(|parent| parent.default_map.clone());
1102        let default_map = match (parent_default_map, self.default_map) {
1103            (Some(mut inherited), Some(child)) => {
1104                for (key, value) in child {
1105                    inherited.insert(key, value);
1106                }
1107                Some(inherited)
1108            }
1109            (Some(inherited), None) => Some(inherited),
1110            (None, Some(child)) => Some(child),
1111            (None, None) => None,
1112        };
1113
1114        Context {
1115            parent: self.parent,
1116            info_name: self.info_name,
1117            params: HashMap::new(),
1118            args: Vec::new(),
1119            obj,
1120            meta,
1121            default_map,
1122            invoked_subcommand: None,
1123            terminal_width,
1124            max_content_width,
1125            allow_extra_args: self.allow_extra_args.unwrap_or(false),
1126            allow_interspersed_args: self.allow_interspersed_args.unwrap_or(true),
1127            ignore_unknown_options: self.ignore_unknown_options.unwrap_or(false),
1128            help_option_names,
1129            resilient_parsing: self.resilient_parsing,
1130            auto_envvar_prefix,
1131            color,
1132            show_default,
1133            parameter_source: HashMap::new(),
1134            close_callbacks: RefCell::new(Vec::new()),
1135        }
1136    }
1137}
1138
1139#[cfg(test)]
1140mod tests {
1141    use super::*;
1142    use std::sync::atomic::{AtomicUsize, Ordering};
1143
1144    #[test]
1145    fn test_context_default_values() {
1146        let ctx = Context::new();
1147
1148        assert!(ctx.parent().is_none());
1149        assert!(ctx.info_name().is_none());
1150        assert!(ctx.params().is_empty());
1151        assert!(ctx.args().is_empty());
1152        assert!(!ctx.allow_extra_args());
1153        assert!(ctx.allow_interspersed_args());
1154        assert!(!ctx.ignore_unknown_options());
1155        assert_eq!(ctx.help_option_names(), &["--help".to_string()]);
1156        assert!(!ctx.resilient_parsing());
1157        assert!(ctx.auto_envvar_prefix().is_none());
1158        assert!(ctx.color().is_none());
1159        assert!(ctx.show_default().is_none());
1160    }
1161
1162    #[test]
1163    fn test_context_builder() {
1164        let ctx = ContextBuilder::new()
1165            .info_name("myapp")
1166            .allow_extra_args(true)
1167            .allow_interspersed_args(false)
1168            .ignore_unknown_options(true)
1169            .terminal_width(120)
1170            .max_content_width(100)
1171            .resilient_parsing(true)
1172            .auto_envvar_prefix("MYAPP")
1173            .color(true)
1174            .show_default(false)
1175            .help_option_names(vec!["--help".to_string(), "-h".to_string()])
1176            .build();
1177
1178        assert_eq!(ctx.info_name(), Some("myapp"));
1179        assert!(ctx.allow_extra_args());
1180        assert!(!ctx.allow_interspersed_args());
1181        assert!(ctx.ignore_unknown_options());
1182        assert_eq!(ctx.terminal_width(), Some(120));
1183        assert_eq!(ctx.max_content_width(), Some(100));
1184        assert!(ctx.resilient_parsing());
1185        assert_eq!(ctx.auto_envvar_prefix(), Some("MYAPP"));
1186        assert_eq!(ctx.color(), Some(true));
1187        assert_eq!(ctx.show_default(), Some(false));
1188        assert_eq!(
1189            ctx.help_option_names(),
1190            &["--help".to_string(), "-h".to_string()]
1191        );
1192    }
1193
1194    #[test]
1195    fn test_parent_child_context_chain() {
1196        let parent = Arc::new(
1197            ContextBuilder::new()
1198                .info_name("cli")
1199                .terminal_width(100)
1200                .color(true)
1201                .build(),
1202        );
1203
1204        let child = ContextBuilder::new()
1205            .info_name("subcommand")
1206            .parent(Arc::clone(&parent))
1207            .build();
1208
1209        // Child inherits from parent
1210        assert_eq!(child.terminal_width(), Some(100));
1211        assert_eq!(child.color(), Some(true));
1212
1213        // Parent reference is correct
1214        assert!(child.parent().is_some());
1215        assert_eq!(child.parent().unwrap().info_name(), Some("cli"));
1216    }
1217
1218    #[test]
1219    fn test_command_path() {
1220        let root = Arc::new(ContextBuilder::new().info_name("cli").build());
1221        let sub = Arc::new(
1222            ContextBuilder::new()
1223                .info_name("group")
1224                .parent(Arc::clone(&root))
1225                .build(),
1226        );
1227        let leaf = ContextBuilder::new()
1228            .info_name("command")
1229            .parent(sub)
1230            .build();
1231
1232        assert_eq!(root.command_path(), "cli");
1233        assert_eq!(leaf.command_path(), "cli group command");
1234    }
1235
1236    #[test]
1237    fn test_find_root() {
1238        let root = Arc::new(ContextBuilder::new().info_name("cli").build());
1239        let child = Arc::new(
1240            ContextBuilder::new()
1241                .info_name("sub")
1242                .parent(Arc::clone(&root))
1243                .build(),
1244        );
1245        let grandchild = ContextBuilder::new()
1246            .info_name("leaf")
1247            .parent(child)
1248            .build();
1249
1250        assert_eq!(grandchild.find_root().info_name(), Some("cli"));
1251    }
1252
1253    #[test]
1254    fn test_thread_local_stack() {
1255        // Ensure stack is empty initially
1256        assert!(get_current_context().is_none());
1257
1258        let ctx1 = Arc::new(ContextBuilder::new().info_name("ctx1").build());
1259        let ctx2 = Arc::new(ContextBuilder::new().info_name("ctx2").build());
1260
1261        // Push first context
1262        push_context(Arc::clone(&ctx1));
1263        assert_eq!(get_current_context().unwrap().info_name(), Some("ctx1"));
1264
1265        // Push second context
1266        push_context(Arc::clone(&ctx2));
1267        assert_eq!(get_current_context().unwrap().info_name(), Some("ctx2"));
1268
1269        // Pop returns the top context
1270        let popped = pop_context();
1271        assert_eq!(popped.unwrap().info_name(), Some("ctx2"));
1272        assert_eq!(get_current_context().unwrap().info_name(), Some("ctx1"));
1273
1274        // Clean up
1275        pop_context();
1276        assert!(get_current_context().is_none());
1277    }
1278
1279    #[test]
1280    fn test_parameter_source_tracking() {
1281        let mut ctx = Context::new();
1282
1283        // Initially no source
1284        assert!(ctx.get_parameter_source("name").is_none());
1285
1286        // Set source
1287        ctx.set_parameter_source("name", ParameterSource::CommandLine);
1288        assert_eq!(
1289            ctx.get_parameter_source("name"),
1290            Some(ParameterSource::CommandLine)
1291        );
1292
1293        // Update source
1294        ctx.set_parameter_source("name", ParameterSource::Environment);
1295        assert_eq!(
1296            ctx.get_parameter_source("name"),
1297            Some(ParameterSource::Environment)
1298        );
1299    }
1300
1301    #[test]
1302    fn test_close_callbacks() {
1303        let counter = Arc::new(AtomicUsize::new(0));
1304        let c1 = Arc::clone(&counter);
1305        let c2 = Arc::clone(&counter);
1306        let c3 = Arc::clone(&counter);
1307
1308        let ctx = Context::new();
1309
1310        // Register callbacks - they record the order they were called
1311        ctx.call_on_close(move || {
1312            c1.fetch_add(1, Ordering::SeqCst);
1313        });
1314        ctx.call_on_close(move || {
1315            c2.fetch_add(10, Ordering::SeqCst);
1316        });
1317        ctx.call_on_close(move || {
1318            c3.fetch_add(100, Ordering::SeqCst);
1319        });
1320
1321        assert_eq!(counter.load(Ordering::SeqCst), 0);
1322
1323        ctx.close();
1324
1325        // All callbacks should have run
1326        assert_eq!(counter.load(Ordering::SeqCst), 111);
1327
1328        // Calling close again should do nothing (callbacks are drained)
1329        ctx.close();
1330        assert_eq!(counter.load(Ordering::SeqCst), 111);
1331    }
1332
1333    #[test]
1334    fn test_error_creation() {
1335        let ctx = ContextBuilder::new().info_name("myapp").build();
1336
1337        let usage_err = ctx.fail("something went wrong");
1338        assert!(matches!(usage_err, ClickError::UsageError { .. }));
1339        assert_eq!(usage_err.exit_code(), 2);
1340
1341        let abort_err = ctx.abort();
1342        assert!(matches!(abort_err, ClickError::Abort));
1343        assert_eq!(abort_err.exit_code(), 1);
1344
1345        let exit_err = ctx.exit(42);
1346        assert!(matches!(exit_err, ClickError::Exit { code: 42 }));
1347        assert_eq!(exit_err.exit_code(), 42);
1348    }
1349
1350    #[test]
1351    fn test_params_access() {
1352        let mut ctx = Context::new();
1353
1354        ctx.params_mut()
1355            .insert("count".to_string(), Arc::new(42i32));
1356        ctx.params_mut()
1357            .insert("name".to_string(), Arc::new("Alice".to_string()));
1358
1359        assert_eq!(ctx.get_param::<i32>("count"), Some(&42));
1360        assert_eq!(ctx.get_param::<String>("name"), Some(&"Alice".to_string()));
1361        assert!(ctx.get_param::<i32>("name").is_none()); // Wrong type
1362        assert!(ctx.get_param::<i32>("missing").is_none()); // Missing
1363    }
1364
1365    #[test]
1366    fn test_obj_access() {
1367        #[derive(Debug, PartialEq)]
1368        struct AppState {
1369            value: i32,
1370        }
1371
1372        let ctx = ContextBuilder::new().obj(AppState { value: 123 }).build();
1373
1374        let state = ctx.obj::<AppState>().unwrap();
1375        assert_eq!(state.value, 123);
1376
1377        // Wrong type returns None
1378        assert!(ctx.obj::<String>().is_none());
1379    }
1380
1381    #[test]
1382    fn test_lookup_default() {
1383        let mut defaults: HashMap<String, BoxedValue> = HashMap::new();
1384        defaults.insert("count".to_string(), Arc::new(42i32));
1385        defaults.insert("name".to_string(), Arc::new("default".to_string()));
1386
1387        let ctx = ContextBuilder::new().default_map(defaults).build();
1388
1389        let count_default = ctx.lookup_default("count").unwrap();
1390        assert_eq!(count_default.downcast_ref::<i32>(), Some(&42));
1391
1392        let name_default = ctx.lookup_default("name").unwrap();
1393        assert_eq!(
1394            name_default.downcast_ref::<String>(),
1395            Some(&"default".to_string())
1396        );
1397
1398        assert!(ctx.lookup_default("missing").is_none());
1399    }
1400
1401    #[test]
1402    fn test_default_map_inheritance() {
1403        let mut parent_defaults: HashMap<String, BoxedValue> = HashMap::new();
1404        parent_defaults.insert("count".to_string(), Arc::new(5i32));
1405
1406        let parent = Arc::new(ContextBuilder::new().default_map(parent_defaults).build());
1407
1408        let mut child_defaults: HashMap<String, BoxedValue> = HashMap::new();
1409        child_defaults.insert("count".to_string(), Arc::new(10i32));
1410        child_defaults.insert("name".to_string(), Arc::new("Bob".to_string()));
1411
1412        let child = ContextBuilder::new()
1413            .parent(Arc::clone(&parent))
1414            .default_map(child_defaults)
1415            .build();
1416
1417        let count_default = child.lookup_default("count").unwrap();
1418        assert_eq!(count_default.downcast_ref::<i32>(), Some(&10));
1419
1420        let name_default = child.lookup_default("name").unwrap();
1421        assert_eq!(
1422            name_default.downcast_ref::<String>(),
1423            Some(&"Bob".to_string())
1424        );
1425    }
1426
1427    #[test]
1428    fn test_auto_envvar_prefix_normalization() {
1429        // Direct prefix is normalized
1430        let ctx = ContextBuilder::new().auto_envvar_prefix("my-app").build();
1431        assert_eq!(ctx.auto_envvar_prefix(), Some("MY_APP"));
1432
1433        // Inherited prefix is expanded
1434        let parent = Arc::new(ContextBuilder::new().auto_envvar_prefix("MY_APP").build());
1435        let child = ContextBuilder::new()
1436            .info_name("sub-cmd")
1437            .parent(parent)
1438            .build();
1439        assert_eq!(child.auto_envvar_prefix(), Some("MY_APP_SUB_CMD"));
1440    }
1441
1442    #[test]
1443    fn test_args_access() {
1444        let mut ctx = Context::new();
1445
1446        assert!(ctx.args().is_empty());
1447
1448        ctx.args_mut().push("extra1".to_string());
1449        ctx.args_mut().push("extra2".to_string());
1450
1451        assert_eq!(ctx.args(), &["extra1", "extra2"]);
1452    }
1453
1454    #[test]
1455    fn test_meta_access() {
1456        let mut ctx = Context::new();
1457
1458        ctx.meta_mut()
1459            .insert("mymodule.key".to_string(), Arc::new(42i32));
1460
1461        assert_eq!(ctx.get_meta::<i32>("mymodule.key"), Some(&42));
1462        assert!(ctx.get_meta::<String>("mymodule.key").is_none()); // Wrong type
1463        assert!(ctx.get_meta::<i32>("other.key").is_none()); // Missing
1464    }
1465
1466    #[test]
1467    fn test_invoked_subcommand() {
1468        let mut ctx = Context::new();
1469
1470        assert!(ctx.invoked_subcommand().is_none());
1471
1472        ctx.set_invoked_subcommand(Some("subcommand".to_string()));
1473        assert_eq!(ctx.invoked_subcommand(), Some("subcommand"));
1474
1475        ctx.set_invoked_subcommand(Some("*".to_string()));
1476        assert_eq!(ctx.invoked_subcommand(), Some("*"));
1477
1478        ctx.set_invoked_subcommand(None);
1479        assert!(ctx.invoked_subcommand().is_none());
1480    }
1481
1482    // =========================================================================
1483    // Tests for Context helper methods: invoke, forward, with_resource
1484    // =========================================================================
1485
1486    #[test]
1487    fn test_context_invoke_command() {
1488        use crate::command::Command;
1489        use std::sync::atomic::{AtomicBool, Ordering};
1490
1491        let invoked = Arc::new(AtomicBool::new(false));
1492        let invoked_clone = Arc::clone(&invoked);
1493
1494        let other_cmd = Command::new("other")
1495            .callback(move |_ctx| {
1496                invoked_clone.store(true, Ordering::SeqCst);
1497                Ok(())
1498            })
1499            .build();
1500
1501        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1502        let result = ctx.invoke(&other_cmd, &[]);
1503
1504        assert!(result.is_ok());
1505        assert!(invoked.load(Ordering::SeqCst));
1506    }
1507
1508    #[test]
1509    fn test_context_invoke_with_args() {
1510        use crate::command::Command;
1511        use crate::argument::Argument;
1512        use std::sync::Mutex;
1513
1514        let captured_name = Arc::new(Mutex::new(String::new()));
1515        let captured_clone = Arc::clone(&captured_name);
1516
1517        let other_cmd = Command::new("greet")
1518            .argument(Argument::new("name").build())
1519            .callback(move |ctx| {
1520                if let Some(name) = ctx.get_param::<String>("name") {
1521                    let mut lock = captured_clone.lock().unwrap();
1522                    *lock = name.clone();
1523                }
1524                Ok(())
1525            })
1526            .build();
1527
1528        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1529        let result = ctx.invoke(&other_cmd, &["Alice".to_string()]);
1530
1531        assert!(result.is_ok());
1532        let name = captured_name.lock().unwrap();
1533        assert_eq!(*name, "Alice");
1534    }
1535
1536    #[test]
1537    fn test_context_invoke_creates_child_context() {
1538        use crate::command::Command;
1539        use std::sync::Mutex;
1540
1541        let parent_name = Arc::new(Mutex::new(None::<String>));
1542        let parent_clone = Arc::clone(&parent_name);
1543
1544        let other_cmd = Command::new("child")
1545            .callback(move |ctx| {
1546                if let Some(parent) = ctx.parent() {
1547                    let mut lock = parent_clone.lock().unwrap();
1548                    *lock = parent.info_name().map(|s| s.to_string());
1549                }
1550                Ok(())
1551            })
1552            .build();
1553
1554        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1555        let result = ctx.invoke(&other_cmd, &[]);
1556
1557        assert!(result.is_ok());
1558        let captured = parent_name.lock().unwrap();
1559        assert_eq!(*captured, Some("main".to_string()));
1560    }
1561
1562    #[test]
1563    fn test_context_forward_copies_params() {
1564        use crate::command::Command;
1565        use std::sync::Mutex;
1566
1567        let forwarded_name = Arc::new(Mutex::new(None::<String>));
1568        let forwarded_clone = Arc::clone(&forwarded_name);
1569
1570        let other_cmd = Command::new("receiver")
1571            .callback(move |ctx| {
1572                let mut lock = forwarded_clone.lock().unwrap();
1573                *lock = ctx.get_param::<String>("name").cloned();
1574                Ok(())
1575            })
1576            .build();
1577
1578        let mut ctx = ContextBuilder::new().info_name("sender").build();
1579        ctx.params_mut().insert("name".to_string(), Arc::new("Forwarded".to_string()));
1580        let ctx = Arc::new(ctx);
1581
1582        let result = ctx.forward(&other_cmd);
1583
1584        assert!(result.is_ok());
1585        let captured = forwarded_name.lock().unwrap();
1586        assert_eq!(*captured, Some("Forwarded".to_string()));
1587    }
1588
1589    #[test]
1590    fn test_context_forward_copies_multiple_params() {
1591        use crate::command::Command;
1592        use std::sync::Mutex;
1593
1594        let forwarded_params = Arc::new(Mutex::new((None::<String>, None::<i32>)));
1595        let params_clone = Arc::clone(&forwarded_params);
1596
1597        let other_cmd = Command::new("receiver")
1598            .callback(move |ctx| {
1599                let mut lock = params_clone.lock().unwrap();
1600                lock.0 = ctx.get_param::<String>("name").cloned();
1601                lock.1 = ctx.get_param::<i32>("count").copied();
1602                Ok(())
1603            })
1604            .build();
1605
1606        let mut ctx = ContextBuilder::new().info_name("sender").build();
1607        ctx.params_mut().insert("name".to_string(), Arc::new("Test".to_string()));
1608        ctx.params_mut().insert("count".to_string(), Arc::new(42i32));
1609        let ctx = Arc::new(ctx);
1610
1611        let result = ctx.forward(&other_cmd);
1612
1613        assert!(result.is_ok());
1614        let captured = forwarded_params.lock().unwrap();
1615        assert_eq!(captured.0, Some("Test".to_string()));
1616        assert_eq!(captured.1, Some(42));
1617    }
1618
1619    #[test]
1620    fn test_context_forward_copies_parameter_sources() {
1621        use crate::command::Command;
1622        use std::sync::Mutex;
1623
1624        let forwarded_source = Arc::new(Mutex::new(None::<ParameterSource>));
1625        let source_clone = Arc::clone(&forwarded_source);
1626
1627        let other_cmd = Command::new("receiver")
1628            .callback(move |ctx| {
1629                let mut lock = source_clone.lock().unwrap();
1630                *lock = ctx.get_parameter_source("name");
1631                Ok(())
1632            })
1633            .build();
1634
1635        let mut ctx = ContextBuilder::new().info_name("sender").build();
1636        ctx.params_mut().insert("name".to_string(), Arc::new("Test".to_string()));
1637        ctx.set_parameter_source("name", ParameterSource::CommandLine);
1638        let ctx = Arc::new(ctx);
1639
1640        let result = ctx.forward(&other_cmd);
1641
1642        assert!(result.is_ok());
1643        let captured = forwarded_source.lock().unwrap();
1644        assert_eq!(*captured, Some(ParameterSource::CommandLine));
1645    }
1646
1647    #[test]
1648    fn test_context_forward_creates_child_context() {
1649        use crate::command::Command;
1650        use std::sync::Mutex;
1651
1652        let parent_name = Arc::new(Mutex::new(None::<String>));
1653        let parent_clone = Arc::clone(&parent_name);
1654
1655        let other_cmd = Command::new("receiver")
1656            .callback(move |ctx| {
1657                if let Some(parent) = ctx.parent() {
1658                    let mut lock = parent_clone.lock().unwrap();
1659                    *lock = parent.info_name().map(|s| s.to_string());
1660                }
1661                Ok(())
1662            })
1663            .build();
1664
1665        let ctx = Arc::new(ContextBuilder::new().info_name("sender").build());
1666        let result = ctx.forward(&other_cmd);
1667
1668        assert!(result.is_ok());
1669        let captured = parent_name.lock().unwrap();
1670        assert_eq!(*captured, Some("sender".to_string()));
1671    }
1672
1673    #[test]
1674    fn test_with_resource_basic() {
1675        use std::sync::atomic::{AtomicBool, Ordering};
1676
1677        struct Resource {
1678            value: i32,
1679        }
1680
1681        let cleaned_up = Arc::new(AtomicBool::new(false));
1682        let cleaned_up_clone = Arc::clone(&cleaned_up);
1683
1684        let ctx = ContextBuilder::new().build();
1685
1686        let result = ctx.with_resource(
1687            Resource { value: 42 },
1688            |res| res.value * 2,
1689            move || {
1690                cleaned_up_clone.store(true, Ordering::SeqCst);
1691            },
1692        );
1693
1694        assert_eq!(result, 84);
1695        assert!(!cleaned_up.load(Ordering::SeqCst)); // Not yet cleaned up
1696
1697        ctx.close();
1698        assert!(cleaned_up.load(Ordering::SeqCst)); // Now cleaned up
1699    }
1700
1701    #[test]
1702    fn test_with_resource_multiple() {
1703        use std::sync::atomic::{AtomicUsize, Ordering};
1704
1705        let cleanup_count = Arc::new(AtomicUsize::new(0));
1706        let count1 = Arc::clone(&cleanup_count);
1707        let count2 = Arc::clone(&cleanup_count);
1708
1709        let ctx = ContextBuilder::new().build();
1710
1711        let result1 = ctx.with_resource(
1712            10,
1713            |res| *res + 5,
1714            move || {
1715                count1.fetch_add(1, Ordering::SeqCst);
1716            },
1717        );
1718
1719        let result2 = ctx.with_resource(
1720            20,
1721            |res| *res * 2,
1722            move || {
1723                count2.fetch_add(1, Ordering::SeqCst);
1724            },
1725        );
1726
1727        assert_eq!(result1, 15);
1728        assert_eq!(result2, 40);
1729        assert_eq!(cleanup_count.load(Ordering::SeqCst), 0);
1730
1731        ctx.close();
1732        assert_eq!(cleanup_count.load(Ordering::SeqCst), 2);
1733    }
1734
1735    #[test]
1736    fn test_with_resource_string_resource() {
1737        use std::sync::atomic::{AtomicBool, Ordering};
1738
1739        let cleaned_up = Arc::new(AtomicBool::new(false));
1740        let cleaned_up_clone = Arc::clone(&cleaned_up);
1741
1742        let ctx = ContextBuilder::new().build();
1743
1744        let result = ctx.with_resource(
1745            String::from("hello"),
1746            |s| s.len(),
1747            move || {
1748                cleaned_up_clone.store(true, Ordering::SeqCst);
1749            },
1750        );
1751
1752        assert_eq!(result, 5);
1753
1754        ctx.close();
1755        assert!(cleaned_up.load(Ordering::SeqCst));
1756    }
1757
1758    #[test]
1759    fn test_invoke_error_propagation() {
1760        use crate::command::Command;
1761        use crate::error::ClickError;
1762
1763        let other_cmd = Command::new("failing")
1764            .callback(|_ctx| {
1765                Err(ClickError::usage("intentional failure"))
1766            })
1767            .build();
1768
1769        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1770        let result = ctx.invoke(&other_cmd, &[]);
1771
1772        assert!(result.is_err());
1773        let err = result.unwrap_err();
1774        assert!(matches!(err, ClickError::UsageError { .. }));
1775    }
1776
1777    #[test]
1778    fn test_forward_error_propagation() {
1779        use crate::command::Command;
1780        use crate::error::ClickError;
1781
1782        let other_cmd = Command::new("failing")
1783            .callback(|_ctx| {
1784                Err(ClickError::usage("intentional failure"))
1785            })
1786            .build();
1787
1788        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1789        let result = ctx.forward(&other_cmd);
1790
1791        assert!(result.is_err());
1792        let err = result.unwrap_err();
1793        assert!(matches!(err, ClickError::UsageError { .. }));
1794    }
1795
1796    #[test]
1797    fn test_invoke_runs_child_close_callbacks() {
1798        use crate::command::Command;
1799        use std::sync::atomic::{AtomicBool, Ordering};
1800
1801        let child_closed = Arc::new(AtomicBool::new(false));
1802        let closed_clone = Arc::clone(&child_closed);
1803
1804        let other_cmd = Command::new("child")
1805            .callback(move |ctx| {
1806                let closed_clone = Arc::clone(&closed_clone);
1807                ctx.call_on_close(move || {
1808                    closed_clone.store(true, Ordering::SeqCst);
1809                });
1810                Ok(())
1811            })
1812            .build();
1813
1814        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1815        let result = ctx.invoke(&other_cmd, &[]);
1816
1817        assert!(result.is_ok());
1818        // Child context should have been closed after invoke
1819        assert!(child_closed.load(Ordering::SeqCst));
1820    }
1821
1822    #[test]
1823    fn test_forward_runs_child_close_callbacks() {
1824        use crate::command::Command;
1825        use std::sync::atomic::{AtomicBool, Ordering};
1826
1827        let child_closed = Arc::new(AtomicBool::new(false));
1828        let closed_clone = Arc::clone(&child_closed);
1829
1830        let other_cmd = Command::new("child")
1831            .callback(move |ctx| {
1832                let closed_clone = Arc::clone(&closed_clone);
1833                ctx.call_on_close(move || {
1834                    closed_clone.store(true, Ordering::SeqCst);
1835                });
1836                Ok(())
1837            })
1838            .build();
1839
1840        let ctx = Arc::new(ContextBuilder::new().info_name("main").build());
1841        let result = ctx.forward(&other_cmd);
1842
1843        assert!(result.is_ok());
1844        // Child context should have been closed after forward
1845        assert!(child_closed.load(Ordering::SeqCst));
1846    }
1847}