Skip to main content

kawauso_process/invocation/
variable.rs

1//! One environment variable of an external command
2
3use std::fmt::Display;
4use std::fmt::Formatter;
5use std::fmt::Result;
6
7use super::variable_name::VariableName;
8use super::variable_value::VariableValue;
9
10/// One environment variable of an external command
11///
12/// A variable carries a name and a value. The command reads the variable in
13/// its environment, next to the variables that it inherits from the process
14/// that runs it. Some programs take a decision from a variable and from no
15/// flag, such as a test runner that selects the format of its report with
16/// one.
17///
18/// A pair of a name and a value becomes a variable, so a caller that holds
19/// its variables as pairs gives them to an invocation as they are.
20///
21/// # Examples
22///
23/// ```
24/// use kawauso_process::invocation::Variable;
25///
26/// let variable = Variable::from(("RUSTUP_TOOLCHAIN", "stable"));
27///
28/// assert_eq!(variable.name().get(), "RUSTUP_TOOLCHAIN");
29/// ```
30#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
31pub struct Variable {
32    /// The name under which the command reads the variable
33    name: VariableName,
34
35    /// The value that the command reads
36    value: VariableValue,
37}
38
39impl Variable {
40    /// Creates a variable from a name and a value
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use kawauso_process::invocation::Variable;
46    ///
47    /// let variable = Variable::new("CARGO_TERM_COLOR", "always");
48    ///
49    /// assert_eq!(variable.value().get(), "always");
50    /// ```
51    pub fn new(name: impl Into<VariableName>, value: impl Into<VariableValue>) -> Self {
52        Self {
53            name: name.into(),
54            value: value.into(),
55        }
56    }
57
58    /// Returns the name of the variable
59    pub fn name(&self) -> &VariableName {
60        &self.name
61    }
62
63    /// Returns the value of the variable
64    pub fn value(&self) -> &VariableValue {
65        &self.value
66    }
67}
68
69/// Shows the variable for a reader
70///
71/// The text is the name, an equals sign, and the value, which is the form in
72/// which a shell sets a variable. A value that holds a space shows without
73/// marks, because the caller that renders a whole command line adds the marks
74/// where the line needs them.
75impl Display for Variable {
76    fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
77        write!(formatter, "{}={}", self.name, self.value)
78    }
79}
80
81/// Creates a variable from a pair of a name and a value
82impl<N: Into<VariableName>, V: Into<VariableValue>> From<(N, V)> for Variable {
83    fn from((name, value): (N, V)) -> Self {
84        Self::new(name, value)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    // An assertion in a test panics by design. A `# Panics` section on every
91    // test would repeat that and give the reader no information.
92    #![allow(clippy::missing_panics_doc)]
93
94    use super::*;
95
96    // A caller that holds its variables as pairs gives them to an invocation
97    // as they are, and the pair keeps its two parts in their places.
98    #[test]
99    fn from_pair_keeps_the_name_and_the_value() {
100        let variable = Variable::from(("RUSTUP_TOOLCHAIN", "stable"));
101
102        assert_eq!(
103            (
104                variable.name().get().to_str(),
105                variable.value().get().to_str()
106            ),
107            (Some("RUSTUP_TOOLCHAIN"), Some("stable"))
108        );
109    }
110
111    // The text of a variable reaches a log line through `Display`, and a
112    // caller that shows one variable gets it in the form of a shell.
113    #[test]
114    fn to_string_returns_the_name_and_the_value() {
115        let variable = Variable::new("RUSTUP_TOOLCHAIN", "stable");
116
117        assert_eq!(variable.to_string(), "RUSTUP_TOOLCHAIN=stable");
118    }
119}