Skip to content

Commit cb36eec

Browse files
committed
Implemented support for workspaces
see #1244
1 parent f53d5dd commit cb36eec

File tree

1 file changed

+116
-16
lines changed

1 file changed

+116
-16
lines changed

src/bin/cargo-fmt.rs

+116-16
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use std::io::Write;
2121
use std::path::PathBuf;
2222
use std::process::{Command, ExitStatus};
2323
use std::str;
24+
use std::collections::HashSet;
25+
use std::iter::FromIterator;
2426

2527
use getopts::Options;
2628
use rustc_serialize::json::Json;
@@ -39,6 +41,11 @@ fn execute() -> i32 {
3941
opts.optflag("h", "help", "show this message");
4042
opts.optflag("q", "quiet", "no output printed to stdout");
4143
opts.optflag("v", "verbose", "use verbose output");
44+
opts.optmulti("p",
45+
"package",
46+
"specify package to format (only usable in workspaces)",
47+
"<package>");
48+
opts.optflag("", "all", "format all packages (only usable in workspaces)");
4249

4350
let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
4451
Ok(m) => m,
@@ -63,7 +70,13 @@ fn execute() -> i32 {
6370
return success;
6471
}
6572

66-
match format_crate(verbosity) {
73+
let workspace_hitlist = match (matches.opt_present("all"), matches.opt_present("p")) {
74+
(false, false) => WorkspaceHitlist::None,
75+
(true, _) => WorkspaceHitlist::All,
76+
(false, true) => WorkspaceHitlist::Some(matches.opt_strs("p")),
77+
};
78+
79+
match format_crate(verbosity, workspace_hitlist) {
6780
Err(e) => {
6881
print_usage(&opts, &e.to_string());
6982
failure
@@ -92,8 +105,10 @@ pub enum Verbosity {
92105
Quiet,
93106
}
94107

95-
fn format_crate(verbosity: Verbosity) -> Result<ExitStatus, std::io::Error> {
96-
let targets = try!(get_targets());
108+
fn format_crate(verbosity: Verbosity,
109+
workspace_hitlist: WorkspaceHitlist)
110+
-> Result<ExitStatus, std::io::Error> {
111+
let targets = try!(get_targets(workspace_hitlist));
97112

98113
// Currently only bin and lib files get formatted
99114
let files: Vec<_> = targets.into_iter()
@@ -139,28 +154,113 @@ pub struct Target {
139154
kind: TargetKind,
140155
}
141156

157+
#[derive(Debug)]
158+
pub enum WorkspaceHitlist {
159+
All,
160+
Some(Vec<String>),
161+
None,
162+
}
163+
164+
impl WorkspaceHitlist {
165+
fn is_all(&self) -> bool {
166+
match *self {
167+
WorkspaceHitlist::All => true,
168+
_ => false,
169+
}
170+
}
171+
172+
fn is_none(&self) -> bool {
173+
match *self {
174+
WorkspaceHitlist::None => true,
175+
_ => false,
176+
}
177+
}
178+
179+
pub fn get_some<'a>(&'a self) -> Option<&'a [String]> {
180+
use std::borrow::Borrow;
181+
match self {
182+
&WorkspaceHitlist::Some(ref hitlist) => Some(hitlist.borrow()),
183+
_ => None,
184+
}
185+
}
186+
}
187+
142188
// Returns a vector of all compile targets of a crate
143-
fn get_targets() -> Result<Vec<Target>, std::io::Error> {
189+
fn get_targets(workspace_hitlist: WorkspaceHitlist) -> Result<Vec<Target>, std::io::Error> {
144190
let mut targets: Vec<Target> = vec![];
145-
let output = try!(Command::new("cargo").arg("read-manifest").output());
191+
if workspace_hitlist.is_none() {
192+
let output = try!(Command::new("cargo").arg("read-manifest").output());
193+
if output.status.success() {
194+
// None of the unwraps should fail if output of `cargo read-manifest` is correct
195+
let data = &String::from_utf8(output.stdout).unwrap();
196+
let json = Json::from_str(data).unwrap();
197+
let jtargets = json.find("targets")
198+
.unwrap()
199+
.as_array()
200+
.unwrap();
201+
for jtarget in jtargets {
202+
targets.push(target_from_json(jtarget));
203+
}
204+
205+
return Ok(targets);
206+
}
207+
return Err(std::io::Error::new(std::io::ErrorKind::NotFound,
208+
str::from_utf8(&output.stderr).unwrap()));
209+
}
210+
// This happens when cargo-fmt is not used inside a crate or
211+
// is used inside a workspace.
212+
// To ensure backward compatability, we only use `cargo metadata` for workspaces.
213+
// TODO: How do we make sure we use either metadata or read-manifest
214+
let output = Command::new("cargo").arg("metadata")
215+
.arg("--no-deps")
216+
.output()?;
146217
if output.status.success() {
147-
// None of the unwraps should fail if output of `cargo read-manifest` is correct
148218
let data = &String::from_utf8(output.stdout).unwrap();
149219
let json = Json::from_str(data).unwrap();
150-
let jtargets = json.find("targets")
220+
let mut hitlist: HashSet<&String> = if !workspace_hitlist.is_all() {
221+
HashSet::from_iter(workspace_hitlist.get_some().unwrap())
222+
} else {
223+
HashSet::new() // Unused
224+
};
225+
let members: Vec<&Json> = json.find("packages")
151226
.unwrap()
152227
.as_array()
153-
.unwrap();
154-
for jtarget in jtargets {
155-
targets.push(target_from_json(jtarget));
228+
.unwrap()
229+
.into_iter()
230+
.filter(|member| if workspace_hitlist.is_all() {
231+
true
232+
} else {
233+
let member_name = member.find("name")
234+
.unwrap()
235+
.as_string()
236+
.unwrap();
237+
if hitlist.take(&member_name.to_string()).is_some() {
238+
true
239+
} else {
240+
false
241+
}
242+
})
243+
.collect();
244+
if hitlist.len() != 0 {
245+
// Mimick cargo of only outputting one <package> spec.
246+
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput,
247+
format!("Could not find package: {:?}",
248+
hitlist.iter().next().unwrap())));
156249
}
157-
158-
Ok(targets)
159-
} else {
160-
// This happens when cargo-fmt is not used inside a crate
161-
Err(std::io::Error::new(std::io::ErrorKind::NotFound,
162-
str::from_utf8(&output.stderr).unwrap()))
250+
for member in members {
251+
let jtargets = member.find("targets")
252+
.unwrap()
253+
.as_array()
254+
.unwrap();
255+
for jtarget in jtargets {
256+
targets.push(target_from_json(jtarget));
257+
}
258+
}
259+
return Ok(targets);
163260
}
261+
Err(std::io::Error::new(std::io::ErrorKind::NotFound,
262+
str::from_utf8(&output.stderr).unwrap()))
263+
164264
}
165265

166266
fn target_from_json(jtarget: &Json) -> Target {

0 commit comments

Comments
 (0)