Skip to content

Commit 33ab1f4

Browse files
committed
Use cargo_metadata crate over json crate
1 parent 58e83fb commit 33ab1f4

File tree

1 file changed

+86
-117
lines changed

1 file changed

+86
-117
lines changed

src/bin/cargo-fmt.rs

Lines changed: 86 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ extern crate getopts;
1818
extern crate serde_json as json;
1919

2020
use std::env;
21+
use std::fs;
2122
use std::hash::{Hash, Hasher};
2223
use std::io::{self, Write};
23-
use std::path::PathBuf;
24+
use std::path::{Path, PathBuf};
2425
use std::process::{Command, ExitStatus};
2526
use std::str;
2627
use std::collections::HashSet;
2728
use std::iter::FromIterator;
2829

2930
use getopts::{Matches, Options};
30-
use json::Value;
3131

3232
fn main() {
3333
let exit_status = execute();
@@ -251,145 +251,104 @@ impl WorkspaceHitlist {
251251
}
252252
}
253253

254-
fn get_cargo_metadata_from_utf8(v: &[u8]) -> Option<Value> {
255-
json::from_str(str::from_utf8(v).ok()?).ok()
256-
}
257-
258-
fn get_json_array_with<'a>(v: &'a Value, key: &str) -> Option<&'a Vec<Value>> {
259-
v.as_object()?.get(key)?.as_array()
260-
}
261-
262-
// `cargo metadata --no-deps | jq '.["packages"]'`
263-
fn get_packages(v: &[u8]) -> Result<Vec<Value>, io::Error> {
264-
let e = io::Error::new(
265-
io::ErrorKind::NotFound,
266-
String::from("`cargo metadata` returned json without a 'packages' key"),
267-
);
268-
match get_cargo_metadata_from_utf8(v) {
269-
Some(ref json_obj) => get_json_array_with(json_obj, "packages").cloned().ok_or(e),
270-
None => Err(e),
271-
}
272-
}
254+
fn get_targets(workspace_hitlist: &WorkspaceHitlist) -> Result<HashSet<Target>, io::Error> {
255+
let mut targets = HashSet::new();
273256

274-
fn extract_target_from_package(package: &Value) -> Option<Vec<Target>> {
275-
let jtargets = get_json_array_with(package, "targets")?;
276-
let mut targets: Vec<Target> = vec![];
277-
for jtarget in jtargets {
278-
targets.push(Target::from_json(jtarget)?);
257+
match *workspace_hitlist {
258+
WorkspaceHitlist::None => get_targets_root_only(&mut targets)?,
259+
WorkspaceHitlist::All => get_targets_recursive(None, &mut targets, &mut HashSet::new())?,
260+
WorkspaceHitlist::Some(ref hitlist) => get_targets_with_hitlist(hitlist, &mut targets)?,
279261
}
280-
Some(targets)
281-
}
282262

283-
fn filter_packages_with_hitlist(
284-
packages: Vec<Value>,
285-
workspace_hitlist: &WorkspaceHitlist,
286-
) -> Result<Vec<Value>, &String> {
287-
let some_hitlist: Option<HashSet<&String>> =
288-
workspace_hitlist.get_some().map(HashSet::from_iter);
289-
if some_hitlist.is_none() {
290-
return Ok(packages);
291-
}
292-
let mut hitlist = some_hitlist.unwrap();
293-
let members: Vec<Value> = packages
294-
.into_iter()
295-
.filter(|member| {
296-
member
297-
.as_object()
298-
.and_then(|member_obj| {
299-
member_obj
300-
.get("name")
301-
.and_then(Value::as_str)
302-
.map(|member_name| {
303-
hitlist.take(&member_name.to_string()).is_some()
304-
})
305-
})
306-
.unwrap_or(false)
307-
})
308-
.collect();
309-
if hitlist.is_empty() {
310-
Ok(members)
263+
if targets.is_empty() {
264+
Err(io::Error::new(
265+
io::ErrorKind::Other,
266+
format!("Failed to find targets"),
267+
))
311268
} else {
312-
Err(hitlist.into_iter().next().unwrap())
269+
Ok(targets)
313270
}
314271
}
315272

316-
fn get_dependencies_from_package(package: &Value) -> Option<Vec<PathBuf>> {
317-
let jdependencies = get_json_array_with(package, "dependencies")?;
318-
let root_path = env::current_dir().ok()?;
319-
let mut dependencies: Vec<PathBuf> = vec![];
320-
for jdep in jdependencies {
321-
let jdependency = jdep.as_object()?;
322-
if !jdependency.get("source")?.is_null() {
323-
continue;
273+
fn get_targets_root_only(targets: &mut HashSet<Target>) -> Result<(), io::Error> {
274+
let metadata = get_cargo_metadata(None)?;
275+
276+
for package in metadata.packages {
277+
for target in package.targets {
278+
if target.name == package.name {
279+
targets.insert(Target::from_target(&target));
280+
}
324281
}
325-
let name = jdependency.get("name")?.as_str()?;
326-
let mut path = root_path.clone();
327-
path.push(&name);
328-
dependencies.push(path);
329282
}
330-
Some(dependencies)
283+
284+
Ok(())
331285
}
332286

333-
// Returns a vector of local dependencies under this crate
334-
fn get_path_to_local_dependencies(packages: &[Value]) -> Vec<PathBuf> {
335-
let mut local_dependencies: Vec<PathBuf> = vec![];
336-
for package in packages {
337-
if let Some(mut d) = get_dependencies_from_package(package) {
338-
local_dependencies.append(&mut d);
287+
fn get_targets_recursive(
288+
manifest_path: Option<&Path>,
289+
mut targets: &mut HashSet<Target>,
290+
visited: &mut HashSet<String>,
291+
) -> Result<(), io::Error> {
292+
let metadata = get_cargo_metadata(manifest_path)?;
293+
294+
for package in metadata.packages {
295+
add_targets(&package.targets, &mut targets);
296+
297+
// Look for local dependencies.
298+
for dependency in package.dependencies {
299+
if dependency.source.is_some() || visited.contains(&dependency.name) {
300+
continue;
301+
}
302+
303+
let mut manifest_path = PathBuf::from(&package.manifest_path);
304+
305+
manifest_path.pop();
306+
manifest_path.push(&dependency.name);
307+
manifest_path.push("Cargo.toml");
308+
309+
if manifest_path.exists() {
310+
visited.insert(dependency.name);
311+
get_targets_recursive(Some(&manifest_path), &mut targets, visited)?;
312+
}
339313
}
340314
}
341-
local_dependencies
315+
316+
Ok(())
342317
}
343318

344-
// Returns a vector of all compile targets of a crate
345-
fn get_targets(workspace_hitlist: &WorkspaceHitlist) -> Result<Vec<Target>, io::Error> {
346-
let output = Command::new("cargo")
347-
.args(&["metadata", "--no-deps", "--format-version=1"])
348-
.output()?;
349-
if output.status.success() {
350-
let cur_dir = env::current_dir()?;
351-
let mut targets: Vec<Target> = vec![];
352-
let packages = get_packages(&output.stdout)?;
353-
354-
// If we can find any local dependencies, we will try to get targets from those as well.
355-
if *workspace_hitlist == WorkspaceHitlist::All {
356-
for path in get_path_to_local_dependencies(&packages) {
357-
match env::set_current_dir(path) {
358-
Ok(..) => match get_targets(workspace_hitlist) {
359-
Ok(ref mut t) => targets.append(t),
360-
Err(..) => continue,
361-
},
362-
Err(..) => continue,
363-
}
364-
}
365-
}
319+
fn get_targets_with_hitlist(
320+
target_names: &[String],
321+
targets: &mut HashSet<Target>,
322+
) -> Result<(), io::Error> {
323+
let metadata = get_cargo_metadata(None)?;
366324

367-
env::set_current_dir(cur_dir)?;
368-
match filter_packages_with_hitlist(packages, workspace_hitlist) {
369-
Ok(packages) => {
370-
for package in packages {
371-
if let Some(mut target) = extract_target_from_package(&package) {
372-
targets.append(&mut target);
373-
}
374-
}
375-
Ok(targets)
376-
}
377-
Err(package) => {
378-
// Mimick cargo of only outputting one <package> spec.
379-
Err(io::Error::new(
380-
io::ErrorKind::InvalidInput,
381-
format!("package `{}` is not a member of the workspace", package),
382-
))
325+
let mut hitlist: HashSet<&String> = HashSet::from_iter(target_names);
326+
327+
for package in metadata.packages {
328+
for target in package.targets {
329+
if hitlist.remove(&target.name) {
330+
targets.insert(Target::from_target(&target));
383331
}
384332
}
333+
}
334+
335+
if hitlist.is_empty() {
336+
Ok(())
385337
} else {
338+
let package = hitlist.iter().next().unwrap();
386339
Err(io::Error::new(
387-
io::ErrorKind::NotFound,
388-
str::from_utf8(&output.stderr).unwrap(),
340+
io::ErrorKind::InvalidInput,
341+
format!("package `{}` is not a member of the workspace", package),
389342
))
390343
}
391344
}
392345

346+
fn add_targets(target_paths: &[cargo_metadata::Target], targets: &mut HashSet<Target>) {
347+
for target in target_paths {
348+
targets.insert(Target::from_target(&target));
349+
}
350+
}
351+
393352
fn format_files(
394353
files: &[PathBuf],
395354
fmt_args: &[String],
@@ -424,3 +383,13 @@ fn format_files(
424383
})?;
425384
command.wait()
426385
}
386+
387+
fn get_cargo_metadata(manifest_path: Option<&Path>) -> Result<cargo_metadata::Metadata, io::Error> {
388+
match cargo_metadata::metadata(manifest_path) {
389+
Ok(metadata) => Ok(metadata),
390+
Err(..) => Err(io::Error::new(
391+
io::ErrorKind::Other,
392+
"`cargo manifest` failed.",
393+
)),
394+
}
395+
}

0 commit comments

Comments
 (0)