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}