]> git.proxmox.com Git - rustc.git/blame - src/tools/cargo/src/cargo/util/config/key.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / cargo / src / cargo / util / config / key.rs
CommitLineData
0a29b90c
FG
1use std::borrow::Cow;
2use std::fmt;
3
4/// Key for a configuration variable.
5///
6/// This type represents a configuration variable that we're looking up in
7/// Cargo's configuration. This structure simultaneously keeps track of a
8/// corresponding environment variable name as well as a TOML config name. The
9/// intention here is that this is built up and torn down over time efficiently,
10/// avoiding clones and such as possible.
11#[derive(Debug, Clone)]
12pub struct ConfigKey {
13 // The current environment variable this configuration key maps to. This is
14 // updated with `push` methods and looks like `CARGO_FOO_BAR` for pushing
15 // `foo` and then `bar`.
16 env: String,
17 // This is used to keep track of how many sub-keys have been pushed on
18 // this `ConfigKey`. Each element of this vector is a new sub-key pushed
19 // onto this `ConfigKey`. Each element is a pair where the first item is
20 // the key part as a string, and the second item is an index into `env`.
21 // The `env` index is used on `pop` to truncate `env` to rewind back to
22 // the previous `ConfigKey` state before a `push`.
23 parts: Vec<(String, usize)>,
24}
25
26impl ConfigKey {
27 /// Creates a new blank configuration key which is ready to get built up by
28 /// using `push` and `push_sensitive`.
29 pub fn new() -> ConfigKey {
30 ConfigKey {
31 env: "CARGO".to_string(),
32 parts: Vec::new(),
33 }
34 }
35
36 /// Creates a `ConfigKey` from the `key` specified.
37 ///
38 /// The `key` specified is expected to be a period-separated toml
39 /// configuration key.
40 pub fn from_str(key: &str) -> ConfigKey {
41 let mut cfg = ConfigKey::new();
42 for part in key.split('.') {
43 cfg.push(part);
44 }
45 cfg
46 }
47
48 /// Pushes a new sub-key on this `ConfigKey`. This sub-key should be
49 /// equivalent to accessing a sub-table in TOML.
50 ///
51 /// Note that this considers `name` to be case-insensitive, meaning that the
781aab86 52 /// corresponding toml key is appended with this `name` as-is and the
0a29b90c
FG
53 /// corresponding env key is appended with `name` after transforming it to
54 /// uppercase characters.
55 pub fn push(&mut self, name: &str) {
56 let env = name.replace("-", "_").to_uppercase();
57 self._push(&env, name);
58 }
59
60 /// Performs the same function as `push` except that the corresponding
61 /// environment variable does not get the uppercase letters of `name` but
62 /// instead `name` is pushed raw onto the corresponding environment
63 /// variable.
64 pub fn push_sensitive(&mut self, name: &str) {
65 self._push(name, name);
66 }
67
68 fn _push(&mut self, env: &str, config: &str) {
69 self.parts.push((config.to_string(), self.env.len()));
70 self.env.push('_');
71 self.env.push_str(env);
72 }
73
74 /// Rewinds this `ConfigKey` back to the state it was at before the last
75 /// `push` method being called.
76 pub fn pop(&mut self) {
77 let (_part, env) = self.parts.pop().unwrap();
78 self.env.truncate(env);
79 }
80
81 /// Returns the corresponding environment variable key for this
82 /// configuration value.
83 pub fn as_env_key(&self) -> &str {
84 &self.env
85 }
86
87 /// Returns an iterator of the key parts as strings.
88 pub(crate) fn parts(&self) -> impl Iterator<Item = &str> {
89 self.parts.iter().map(|p| p.0.as_ref())
90 }
91
92 /// Returns whether or not this is a key for the root table.
93 pub fn is_root(&self) -> bool {
94 self.parts.is_empty()
95 }
96}
97
98impl fmt::Display for ConfigKey {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 let parts: Vec<_> = self.parts().map(|part| escape_key_part(part)).collect();
101 parts.join(".").fmt(f)
102 }
103}
104
105fn escape_key_part<'a>(part: &'a str) -> Cow<'a, str> {
106 let ok = part.chars().all(|c| {
107 matches!(c,
108 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')
109 });
110 if ok {
111 Cow::Borrowed(part)
112 } else {
113 // This is a bit messy, but toml doesn't expose a function to do this.
114 Cow::Owned(toml::Value::from(part).to_string())
115 }
116}