From 5793f3871d5cc5452068040b1dd382d90cf51af8 Mon Sep 17 00:00:00 2001 From: James Kay Date: Tue, 17 Oct 2017 16:52:32 +0100 Subject: [PATCH] add JSON output capabilities --- Cargo.lock | 3 +++ Cargo.toml | 3 +++ src/main.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9e40cbe..e5bc738 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,9 @@ dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 630a82b..fb45ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ cargo = "0.20" env_logger = "0.4" petgraph = "0.4" rustc-serialize = "0.3" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" diff --git a/src/main.rs b/src/main.rs index 765e253..dfab677 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,9 @@ extern crate cargo; extern crate env_logger; extern crate petgraph; extern crate rustc_serialize; +extern crate serde; +#[macro_use] extern crate serde_derive; +extern crate serde_json; use cargo::{Config, CliResult}; use cargo::core::{PackageId, Package, Resolve, Workspace}; @@ -16,7 +19,7 @@ use cargo::util::{self, important_paths, CargoResult, Cfg, CargoError}; use petgraph::EdgeDirection; use petgraph::graph::NodeIndex; use petgraph::visit::EdgeRef; -use std::collections::{HashSet, HashMap}; +use std::collections::{HashSet, HashMap, BTreeMap}; use std::collections::hash_map::Entry; use std::env; use std::str::{self, FromStr}; @@ -50,6 +53,7 @@ Options: --charset CHARSET Set the character set to use in output. Valid values: utf8, ascii [default: utf8] -f, --format FORMAT Format string for printing dependencies + -j, --json Print a JSON representation of the tree --manifest-path PATH Path to the manifest to analyze -v, --verbose Use verbose output -q, --quiet No output printed to stdout other than the tree @@ -71,6 +75,7 @@ struct Flags { flag_no_indent: bool, flag_all: bool, flag_charset: Charset, + flag_json: bool, flag_format: Option, flag_manifest_path: Option, flag_verbose: u32, @@ -214,6 +219,14 @@ fn real_main(flags: Flags, config: &Config) -> CliResult { flags.flag_all); println!(""); } + } else if flags.flag_json { + println!("{}", serde_json::to_string_pretty( + &graph_to_tree(package.package_id(), + &packages, + &resolve, + kind, + &graph, + direction)?).expect("serialization failed")) } else { print_tree(root, kind, @@ -317,6 +330,13 @@ struct Graph<'a> { nodes: HashMap<&'a PackageId, NodeIndex>, } +#[derive(Debug, Serialize, Deserialize)] +struct Tree { + version: String, + features: Vec, + dependencies: BTreeMap>, +} + fn build_graph<'a>(resolve: &'a Resolve, packages: &'a PackageSet, root: &'a PackageId, @@ -368,6 +388,57 @@ fn build_graph<'a>(resolve: &'a Resolve, Ok(graph) } +fn graph_to_tree<'a>(package_id: &'a PackageId, + packages: &'a PackageSet, + resolve: &'a Resolve, + kind: Kind, + graph: &Graph<'a>, + direction: EdgeDirection) -> CargoResult { + let mut visited_deps = HashSet::new(); + graph_to_tree_(package_id, + packages, + resolve, + kind, + graph, + direction, + &mut visited_deps) +} + +fn graph_to_tree_<'a>(package_id: &'a PackageId, + packages: &'a PackageSet, + resolve: &'a Resolve, + kind: Kind, + graph: &Graph<'a>, + direction: EdgeDirection, + visited_deps: &mut HashSet<&'a PackageId>) + -> CargoResult { + let deps = graph.graph + .edges_directed(graph.nodes[package_id], direction) + .filter(|edge| edge.weight() == &kind) + .map(|edge| { + match direction { + EdgeDirection::Incoming => &graph.graph[edge.source()], + EdgeDirection::Outgoing => &graph.graph[edge.target()], + } + }); + + let mut deps_ = BTreeMap::new(); + for dep in deps { + let name = packages.get(dep.id)?.name().to_owned(); + deps_.insert(name, Box::new(graph_to_tree_( + dep.id, packages, resolve, kind, graph, direction, visited_deps)?)); + } + + let package = packages.get(package_id)?; + + Ok(Tree { + version: package.version().to_string(), + features: resolve.features_sorted(package_id) + .into_iter().map(|x| x.to_owned()).collect(), + dependencies: deps_, + }) +} + fn print_tree<'a>(package: &'a PackageId, kind: Kind, graph: &Graph<'a>,