1use std::env;
2use std::path::{Path, PathBuf};
3
4use anyhow::{anyhow, Result};
5
6pub struct Args {
7 all: Vec<String>,
8 target: Option<String>,
9 manifest_path: Option<PathBuf>,
10 verbosity: Option<Verbosity>,
11}
12
13#[derive(Debug, Eq, PartialEq)]
14pub enum Verbosity {
15 Quiet,
16 Verbose,
17}
18
19impl Args {
20 pub fn new<T, P, A, S>(
22 target: Option<T>,
23 manifest_path: Option<P>,
24 verbosity: Option<Verbosity>,
25 other_args: A,
26 ) -> Result<Self>
27 where
28 T: Into<String> + Clone,
29 P: AsRef<Path>,
30 A: IntoIterator<Item = S>,
31 S: AsRef<str>,
32 {
33 let other_args = other_args
34 .into_iter()
35 .map(|a| a.as_ref().to_string())
36 .collect::<Vec<_>>();
37
38 let explicit_args = ["--target", "--manifest-path", "--verbose", "--quiet"];
40 let duplicates = other_args
41 .iter()
42 .filter(|a| {
43 explicit_args
44 .iter()
45 .any(|ea| a == ea || a.starts_with(&format!("{}=", ea)))
46 })
47 .collect::<Vec<_>>();
48 if !duplicates.is_empty() {
49 return Err(anyhow!(
50 "The following args should be passed explicitly: {:?}",
51 duplicates
52 ));
53 }
54
55 let mut all = other_args;
57 if let Some(target) = target.clone() {
58 all.push(format!("--target={}", target.into()))
59 }
60 if let Some(ref manifest_path) = manifest_path {
61 all.push(format!(
62 "--manifest-path={}",
63 manifest_path.as_ref().to_string_lossy()
64 ))
65 }
66 if let Some(ref verbosity) = verbosity {
67 match verbosity {
68 Verbosity::Verbose => all.push("--verbose".into()),
69 Verbosity::Quiet => all.push("--quiet".into()),
70 }
71 }
72
73 Ok(Args {
74 all,
75 target: target.map(Into::into),
76 manifest_path: manifest_path.map(|p| p.as_ref().into()),
77 verbosity,
78 })
79 }
80
81 pub fn from_raw<A, S>(all: A) -> Result<Self>
83 where
84 A: IntoIterator<Item = S>,
85 S: AsRef<str>,
86 {
87 let all = all
88 .into_iter()
89 .map(|a| a.as_ref().to_string())
90 .collect::<Vec<_>>();
91
92 let mut target: Option<String> = None;
93 let mut manifest_path = None;
94 let mut verbosity = None;
95 {
96 let mut args = all.iter();
97 while let Some(arg) = args.next() {
98 if arg == "--target" {
99 target = args.next().map(|s| s.to_owned());
100 } else if arg.starts_with("--target=") {
101 target = arg.splitn(2, '=').nth(1).map(|s| s.to_owned());
102 }
103 if arg == "--manifest-path" {
104 manifest_path = args.next().map(|s| s.to_owned());
105 } else if arg.starts_with("--manifest-path=") {
106 manifest_path = arg.splitn(2, '=').nth(1).map(|s| s.to_owned());
107 }
108 if arg == "--verbose" || arg == "-v" || arg == "-vv" {
109 if let Some(Verbosity::Quiet) = verbosity {
110 return Err(anyhow!("cannot set both --verbose and --quiet"));
111 }
112 verbosity = Some(Verbosity::Verbose)
113 }
114 if arg == "--quiet" || arg == "-q" {
115 if let Some(Verbosity::Verbose) = verbosity {
116 return Err(anyhow!("cannot set both --verbose and --quiet"));
117 }
118 verbosity = Some(Verbosity::Quiet)
119 }
120 }
121 }
122
123 Ok(Args {
124 all,
125 target: target.map(Into::into),
126 manifest_path: manifest_path.map(Into::into),
127 verbosity,
128 })
129 }
130
131 pub fn all(&self) -> &[String] {
132 &self.all
133 }
134
135 pub fn target(&self) -> Option<&str> {
136 self.target.as_ref().map(|s| &**s)
137 }
138
139 pub fn manifest_path(&self) -> Option<&Path> {
140 self.manifest_path.as_ref().map(|s| &**s)
141 }
142
143 pub fn quiet(&self) -> bool {
144 self.verbosity == Some(Verbosity::Quiet)
145 }
146
147 pub fn verbose(&self) -> bool {
148 self.verbosity == Some(Verbosity::Verbose)
149 }
150}
151
152pub fn args(command_name: &str) -> Result<(Command, Args)> {
153 let mut args = env::args().skip(1);
154 if args.next() != Some("x".to_string() + command_name) {
155 Err(anyhow!(
156 "must be invoked as cargo subcommand: `cargo x{}`",
157 command_name
158 ))?;
159 }
160 let all = args.collect::<Vec<_>>();
161 let command = match all.first().map(|s| s.as_str()) {
162 Some("-h") | Some("--help") => Command::Help,
163 Some("-v") | Some("--version") => Command::Version,
164 _ => Command::Build,
165 };
166
167 let args = Args::from_raw(all)?;
168 Ok((command, args))
169}
170
171#[derive(Clone, PartialEq)]
172pub enum Command {
173 Build,
174 Help,
175 Version,
176}