click/source.rs
1//! Parameter source tracking for click-rs.
2//!
3//! This module provides the [`ParameterSource`] enum which indicates where a
4//! parameter's value originated from. This is useful for debugging and for
5//! implementing precedence rules when values can come from multiple sources.
6
7use std::cmp::Ordering;
8use std::fmt;
9
10/// Indicates the source of a parameter's value.
11///
12/// Use [`Context::get_parameter_source`] to get the source for a parameter by name.
13///
14/// # Precedence
15///
16/// Sources have a defined precedence order (highest to lowest):
17/// 1. [`CommandLine`](ParameterSource::CommandLine) - Explicit user input
18/// 2. [`Environment`](ParameterSource::Environment) - Environment variables
19/// 3. [`DefaultMap`](ParameterSource::DefaultMap) - Context default map
20/// 4. [`Default`](ParameterSource::Default) - Parameter default value
21///
22/// [`Prompt`](ParameterSource::Prompt) is considered equal to `CommandLine` for precedence
23/// since both represent direct user input.
24///
25/// # Example
26///
27/// ```
28/// use click::ParameterSource;
29///
30/// let source = ParameterSource::CommandLine;
31/// assert!(source.is_from_user());
32///
33/// let env_source = ParameterSource::Environment;
34/// assert!(!env_source.is_from_user());
35///
36/// // CommandLine has higher precedence than Environment
37/// assert!(ParameterSource::CommandLine > ParameterSource::Environment);
38/// ```
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40#[non_exhaustive]
41pub enum ParameterSource {
42 /// The value was provided by command line arguments.
43 CommandLine,
44
45 /// The value was provided via an environment variable.
46 Environment,
47
48 /// The value used the default specified by the parameter.
49 Default,
50
51 /// The value used a default provided by the context's default_map.
52 DefaultMap,
53
54 /// The value was provided via an interactive prompt.
55 Prompt,
56}
57
58impl ParameterSource {
59 /// Returns `true` if the value came from direct user input.
60 ///
61 /// This returns `true` for [`CommandLine`](ParameterSource::CommandLine)
62 /// and [`Prompt`](ParameterSource::Prompt), as both represent values
63 /// explicitly provided by the user during execution.
64 ///
65 /// # Example
66 ///
67 /// ```
68 /// use click::ParameterSource;
69 ///
70 /// assert!(ParameterSource::CommandLine.is_from_user());
71 /// assert!(ParameterSource::Prompt.is_from_user());
72 /// assert!(!ParameterSource::Environment.is_from_user());
73 /// assert!(!ParameterSource::Default.is_from_user());
74 /// assert!(!ParameterSource::DefaultMap.is_from_user());
75 /// ```
76 #[inline]
77 pub fn is_from_user(&self) -> bool {
78 matches!(self, Self::CommandLine | Self::Prompt)
79 }
80
81 /// Returns the precedence value for ordering.
82 ///
83 /// Higher values indicate higher precedence (more authoritative sources).
84 #[inline]
85 fn precedence(&self) -> u8 {
86 match self {
87 // User input has highest precedence
88 Self::CommandLine | Self::Prompt => 4,
89 // Environment variables are next
90 Self::Environment => 3,
91 // Context default_map overrides parameter defaults
92 Self::DefaultMap => 2,
93 // Parameter defaults have lowest precedence
94 Self::Default => 1,
95 }
96 }
97}
98
99impl fmt::Display for ParameterSource {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 match self {
102 Self::CommandLine => write!(f, "command line"),
103 Self::Environment => write!(f, "environment variable"),
104 Self::Default => write!(f, "default value"),
105 Self::DefaultMap => write!(f, "default map"),
106 Self::Prompt => write!(f, "prompt"),
107 }
108 }
109}
110
111impl PartialOrd for ParameterSource {
112 #[inline]
113 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
114 Some(self.cmp(other))
115 }
116}
117
118impl Ord for ParameterSource {
119 #[inline]
120 fn cmp(&self, other: &Self) -> Ordering {
121 self.precedence().cmp(&other.precedence())
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_is_from_user() {
131 assert!(ParameterSource::CommandLine.is_from_user());
132 assert!(ParameterSource::Prompt.is_from_user());
133 assert!(!ParameterSource::Environment.is_from_user());
134 assert!(!ParameterSource::Default.is_from_user());
135 assert!(!ParameterSource::DefaultMap.is_from_user());
136 }
137
138 #[test]
139 fn test_display() {
140 assert_eq!(ParameterSource::CommandLine.to_string(), "command line");
141 assert_eq!(
142 ParameterSource::Environment.to_string(),
143 "environment variable"
144 );
145 assert_eq!(ParameterSource::Default.to_string(), "default value");
146 assert_eq!(ParameterSource::DefaultMap.to_string(), "default map");
147 assert_eq!(ParameterSource::Prompt.to_string(), "prompt");
148 }
149
150 #[test]
151 fn test_precedence_ordering() {
152 // CommandLine > Environment > DefaultMap > Default
153 assert!(ParameterSource::CommandLine > ParameterSource::Environment);
154 assert!(ParameterSource::Environment > ParameterSource::DefaultMap);
155 assert!(ParameterSource::DefaultMap > ParameterSource::Default);
156
157 // Prompt equals CommandLine in precedence
158 assert_eq!(
159 ParameterSource::CommandLine.cmp(&ParameterSource::Prompt),
160 Ordering::Equal
161 );
162 assert!(ParameterSource::Prompt > ParameterSource::Environment);
163 }
164
165 #[test]
166 fn test_equality() {
167 assert_eq!(ParameterSource::CommandLine, ParameterSource::CommandLine);
168 assert_ne!(ParameterSource::CommandLine, ParameterSource::Environment);
169 }
170
171 #[test]
172 fn test_copy_clone() {
173 let source = ParameterSource::CommandLine;
174 let copied = source; // Copy
175 let cloned = source.clone(); // Clone
176 assert_eq!(source, copied);
177 assert_eq!(source, cloned);
178 }
179
180 #[test]
181 fn test_hash() {
182 use std::collections::HashSet;
183
184 let mut set = HashSet::new();
185 set.insert(ParameterSource::CommandLine);
186 set.insert(ParameterSource::Environment);
187 set.insert(ParameterSource::CommandLine); // Duplicate
188
189 assert_eq!(set.len(), 2);
190 assert!(set.contains(&ParameterSource::CommandLine));
191 assert!(set.contains(&ParameterSource::Environment));
192 }
193
194 #[test]
195 fn test_debug() {
196 let debug_str = format!("{:?}", ParameterSource::CommandLine);
197 assert_eq!(debug_str, "CommandLine");
198 }
199}