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