Skip to content

Commit 6971b4b

Browse files
committedMar 30, 2017
Implemented support for workspaces
closes #1244
1 parent 603f26d commit 6971b4b

File tree

1 file changed

+96
-16
lines changed

1 file changed

+96
-16
lines changed
 

‎src/bin/cargo-fmt.rs

+96-16
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ 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

25-
use getopts::Options;
27+
use getopts::{Options, Matches};
2628
use rustc_serialize::json::Json;
2729

2830
fn main() {
@@ -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,9 @@ fn execute() -> i32 {
6370
return success;
6471
}
6572

66-
match format_crate(verbosity) {
73+
let workspace_hitlist = WorkspaceHitlist::from_matches(&matches);
74+
75+
match format_crate(verbosity, workspace_hitlist) {
6776
Err(e) => {
6877
print_usage(&opts, &e.to_string());
6978
failure
@@ -92,8 +101,10 @@ pub enum Verbosity {
92101
Quiet,
93102
}
94103

95-
fn format_crate(verbosity: Verbosity) -> Result<ExitStatus, std::io::Error> {
96-
let targets = try!(get_targets());
104+
fn format_crate(verbosity: Verbosity,
105+
workspace_hitlist: WorkspaceHitlist)
106+
-> Result<ExitStatus, std::io::Error> {
107+
let targets = try!(get_targets(workspace_hitlist));
97108

98109
// Currently only bin and lib files get formatted
99110
let files: Vec<_> = targets
@@ -140,25 +151,94 @@ pub struct Target {
140151
kind: TargetKind,
141152
}
142153

154+
#[derive(Debug, PartialEq, Eq)]
155+
pub enum WorkspaceHitlist {
156+
All,
157+
Some(Vec<String>),
158+
None,
159+
}
160+
161+
impl WorkspaceHitlist {
162+
pub fn get_some<'a>(&'a self) -> Option<&'a [String]> {
163+
if let &WorkspaceHitlist::Some(ref hitlist) = self {
164+
Some(&hitlist)
165+
} else {
166+
None
167+
}
168+
}
169+
170+
pub fn from_matches(matches: &Matches) -> WorkspaceHitlist {
171+
match (matches.opt_present("all"), matches.opt_present("p")) {
172+
(false, false) => WorkspaceHitlist::None,
173+
(true, _) => WorkspaceHitlist::All,
174+
(false, true) => WorkspaceHitlist::Some(matches.opt_strs("p")),
175+
}
176+
}
177+
}
178+
143179
// Returns a vector of all compile targets of a crate
144-
fn get_targets() -> Result<Vec<Target>, std::io::Error> {
180+
fn get_targets(workspace_hitlist: WorkspaceHitlist) -> Result<Vec<Target>, std::io::Error> {
145181
let mut targets: Vec<Target> = vec![];
146-
let output = try!(Command::new("cargo").arg("read-manifest").output());
182+
if workspace_hitlist == WorkspaceHitlist::None {
183+
let output = try!(Command::new("cargo").arg("read-manifest").output());
184+
if output.status.success() {
185+
// None of the unwraps should fail if output of `cargo read-manifest` is correct
186+
let data = &String::from_utf8(output.stdout).unwrap();
187+
let json = Json::from_str(data).unwrap();
188+
let jtargets = json.find("targets").unwrap().as_array().unwrap();
189+
for jtarget in jtargets {
190+
targets.push(target_from_json(jtarget));
191+
}
192+
193+
return Ok(targets);
194+
}
195+
return Err(std::io::Error::new(std::io::ErrorKind::NotFound,
196+
str::from_utf8(&output.stderr).unwrap()));
197+
}
198+
// This happens when cargo-fmt is not used inside a crate or
199+
// is used inside a workspace.
200+
// To ensure backward compatability, we only use `cargo metadata` for workspaces.
201+
// TODO: Is it possible only use metadata or read-manifest
202+
let output = Command::new("cargo").arg("metadata")
203+
.arg("--no-deps")
204+
.output()?;
147205
if output.status.success() {
148-
// None of the unwraps should fail if output of `cargo read-manifest` is correct
149206
let data = &String::from_utf8(output.stdout).unwrap();
150207
let json = Json::from_str(data).unwrap();
151-
let jtargets = json.find("targets").unwrap().as_array().unwrap();
152-
for jtarget in jtargets {
153-
targets.push(target_from_json(jtarget));
208+
let mut hitlist: HashSet<&String> = if workspace_hitlist != WorkspaceHitlist::All {
209+
HashSet::from_iter(workspace_hitlist.get_some().unwrap())
210+
} else {
211+
HashSet::new() // Unused
212+
};
213+
let members: Vec<&Json> = json.find("packages")
214+
.unwrap()
215+
.as_array()
216+
.unwrap()
217+
.into_iter()
218+
.filter(|member| if workspace_hitlist == WorkspaceHitlist::All {
219+
true
220+
} else {
221+
let member_name = member.find("name").unwrap().as_string().unwrap();
222+
hitlist.take(&member_name.to_string()).is_some()
223+
})
224+
.collect();
225+
if hitlist.len() != 0 {
226+
// Mimick cargo of only outputting one <package> spec.
227+
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput,
228+
format!("package `{}` is not a member of the workspace",
229+
hitlist.iter().next().unwrap())));
154230
}
155-
156-
Ok(targets)
157-
} else {
158-
// This happens when cargo-fmt is not used inside a crate
159-
Err(std::io::Error::new(std::io::ErrorKind::NotFound,
160-
str::from_utf8(&output.stderr).unwrap()))
231+
for member in members {
232+
let jtargets = member.find("targets").unwrap().as_array().unwrap();
233+
for jtarget in jtargets {
234+
targets.push(target_from_json(jtarget));
235+
}
236+
}
237+
return Ok(targets);
161238
}
239+
Err(std::io::Error::new(std::io::ErrorKind::NotFound,
240+
str::from_utf8(&output.stderr).unwrap()))
241+
162242
}
163243

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

0 commit comments

Comments
 (0)
Please sign in to comment.