Skip to main content

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}