cargo/util/context/
path.rs

1use super::{GlobalContext, StringList, Value};
2use serde::{de::Error, Deserialize};
3use std::path::PathBuf;
4
5/// Use with the `get` API to fetch a string that will be converted to a
6/// `PathBuf`. Relative paths are converted to absolute paths based on the
7/// location of the config file.
8#[derive(Debug, Deserialize, PartialEq, Clone)]
9#[serde(transparent)]
10pub struct ConfigRelativePath(Value<String>);
11
12impl ConfigRelativePath {
13    pub fn new(path: Value<String>) -> ConfigRelativePath {
14        ConfigRelativePath(path)
15    }
16
17    /// Returns the underlying value.
18    pub fn value(&self) -> &Value<String> {
19        &self.0
20    }
21
22    /// Returns the raw underlying configuration value for this key.
23    pub fn raw_value(&self) -> &str {
24        &self.0.val
25    }
26
27    /// Resolves this configuration-relative path to an absolute path.
28    ///
29    /// This will always return an absolute path where it's relative to the
30    /// location for configuration for this value.
31    pub fn resolve_path(&self, gctx: &GlobalContext) -> PathBuf {
32        self.0.definition.root(gctx).join(&self.0.val)
33    }
34
35    /// Resolves this configuration-relative path to either an absolute path or
36    /// something appropriate to execute from `PATH`.
37    ///
38    /// Values which don't look like a filesystem path (don't contain `/` or
39    /// `\`) will be returned as-is, and everything else will fall through to an
40    /// absolute path.
41    pub fn resolve_program(&self, gctx: &GlobalContext) -> PathBuf {
42        gctx.string_to_path(&self.0.val, &self.0.definition)
43    }
44}
45
46/// A config type that is a program to run.
47///
48/// This supports a list of strings like `['/path/to/program', 'somearg']`
49/// or a space separated string like `'/path/to/program somearg'`.
50///
51/// This expects the first value to be the path to the program to run.
52/// Subsequent values are strings of arguments to pass to the program.
53///
54/// Typically you should use `ConfigRelativePath::resolve_program` on the path
55/// to get the actual program.
56///
57/// **Note**: Any usage of this type in config needs to be listed in
58/// the `util::context::is_nonmergable_list` check to prevent list merging
59/// from multiple config files.
60#[derive(Debug, Clone, PartialEq)]
61pub struct PathAndArgs {
62    pub path: ConfigRelativePath,
63    pub args: Vec<String>,
64}
65
66impl<'de> serde::Deserialize<'de> for PathAndArgs {
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: serde::Deserializer<'de>,
70    {
71        let vsl = Value::<StringList>::deserialize(deserializer)?;
72        let mut strings = vsl.val.0;
73        if strings.is_empty() {
74            return Err(D::Error::invalid_length(0, &"at least one element"));
75        }
76        let first = strings.remove(0);
77        let crp = Value {
78            val: first,
79            definition: vsl.definition,
80        };
81        Ok(PathAndArgs {
82            path: ConfigRelativePath(crp),
83            args: strings,
84        })
85    }
86}
87
88impl PathAndArgs {
89    /// Construct a `PathAndArgs` from a string. The string will be split on ascii whitespace,
90    /// with the first item being treated as a `ConfigRelativePath` to the executable, and subsequent
91    /// items as arguments.
92    pub fn from_whitespace_separated_string(p: &Value<String>) -> PathAndArgs {
93        let mut iter = p.val.split_ascii_whitespace().map(str::to_string);
94        let val = iter.next().unwrap_or_default();
95        let args = iter.collect();
96        let crp = Value {
97            val,
98            definition: p.definition.clone(),
99        };
100        PathAndArgs {
101            path: ConfigRelativePath(crp),
102            args,
103        }
104    }
105}