Skip to main content

cargo/config/
definition.rs

1use std::cmp::Ordering;
2use std::fmt;
3use std::mem;
4use std::path::{Path, PathBuf};
5
6/// The kind of source where a config value is defined.
7#[derive(Clone, Copy, Debug)]
8pub enum DefinitionKind {
9    /// A built-in value.
10    BuiltIn,
11    /// Loaded due to config file discovery.
12    Path,
13    /// A value defined in an environment variable.
14    Environment,
15    /// Loaded due to a request from the global cli arg `--config`
16    ///
17    /// Configs loaded via `include` are also seen as from cli args,
18    /// if the initial config is being loaded from cli.
19    Cli,
20}
21
22/// Location where a config value is defined.
23#[derive(Clone, Debug, Eq, Default)]
24pub enum Definition {
25    #[default]
26    BuiltIn,
27    /// Defined in a `.cargo/config`, includes the path to the file.
28    Path(PathBuf),
29    /// Defined in an environment variable, includes the environment key.
30    Environment(String),
31    /// Passed in on the command line.
32    /// A path is attached when the config value is a path to a config file.
33    Cli(Option<PathBuf>),
34}
35
36impl PartialOrd for Definition {
37    fn partial_cmp(&self, other: &Definition) -> Option<Ordering> {
38        Some(self.cmp(other))
39    }
40}
41
42impl Ord for Definition {
43    fn cmp(&self, other: &Definition) -> Ordering {
44        if mem::discriminant(self) == mem::discriminant(other) {
45            Ordering::Equal
46        } else if self.is_higher_priority(other) {
47            Ordering::Greater
48        } else {
49            Ordering::Less
50        }
51    }
52}
53
54impl Definition {
55    /// Root directory where this is defined.
56    ///
57    /// If from a file, it is the directory above `.cargo/config.toml`.
58    /// CLI and env use the provided current working directory.
59    pub fn root<'a>(&'a self, cwd: &'a Path) -> &'a Path {
60        match self {
61            Definition::Path(p) | Definition::Cli(Some(p)) => p.parent().unwrap().parent().unwrap(),
62            Definition::Environment(_) | Definition::Cli(None) | Definition::BuiltIn => cwd,
63        }
64    }
65
66    /// Returns true if self is a higher priority to other.
67    ///
68    /// CLI is preferred over environment, which is preferred over files.
69    pub fn is_higher_priority(&self, other: &Definition) -> bool {
70        matches!(
71            (self, other),
72            (Definition::Cli(_), Definition::Environment(_))
73                | (Definition::Cli(_), Definition::Path(_))
74                | (Definition::Cli(_), Definition::BuiltIn)
75                | (Definition::Environment(_), Definition::Path(_))
76                | (Definition::Environment(_), Definition::BuiltIn)
77                | (Definition::Path(_), Definition::BuiltIn)
78        )
79    }
80}
81
82impl PartialEq for Definition {
83    fn eq(&self, other: &Definition) -> bool {
84        // configuration values are equivalent no matter where they're defined,
85        // but they need to be defined in the same location. For example if
86        // they're defined in the environment that's different than being
87        // defined in a file due to path interpretations.
88        mem::discriminant(self) == mem::discriminant(other)
89    }
90}
91
92impl fmt::Display for Definition {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        match self {
95            Definition::Path(p) | Definition::Cli(Some(p)) => p.display().fmt(f),
96            Definition::Environment(key) => write!(f, "environment variable `{}`", key),
97            Definition::Cli(None) => write!(f, "--config cli option"),
98            Definition::BuiltIn => write!(f, "default"),
99        }
100    }
101}