Skip to content
This repository was archived by the owner on May 12, 2020. It is now read-only.

Commit 8661c2f

Browse files
committed
fix(Dev Deps): fixes a bug where dev deps are not properly filtered
Closes #4
1 parent c861164 commit 8661c2f

File tree

4 files changed

+136
-88
lines changed

4 files changed

+136
-88
lines changed

src/dep.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::io::{Result, Write};
22

33
use config::Config;
44

5-
#[derive(Debug, PartialEq)]
5+
#[derive(Debug, Copy, Clone, PartialEq)]
66
pub enum DepKind {
77
Build,
88
Dev,

src/graph.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,54 @@ impl<'c, 'o> DepGraph<'c, 'o> {
6262
None
6363
}
6464

65+
pub fn update_style(&mut self, name: &str, kind: DepKind) {
66+
if let Some(id) = self.find(name) {
67+
if let Some(dep) = self.get_mut(id) {
68+
dep.kind = kind;
69+
}
70+
}
71+
}
72+
73+
pub fn remove(&mut self, name: &str) {
74+
if let Some(id) = self.find(name) {
75+
debugln!("Removing node: {} index {}", name, id);
76+
self.nodes.remove(id);
77+
}
78+
}
79+
80+
pub fn remove_orphans(&mut self) {
81+
let len = self.nodes.len();
82+
loop {
83+
let mut to_rem = None;
84+
for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
85+
if idl > len || idr > len {
86+
to_rem = Some(eid);
87+
}
88+
}
89+
if let Some(id) = to_rem {
90+
self.edges.remove(id);
91+
continue;
92+
}
93+
break;
94+
}
95+
}
96+
97+
pub fn get_mut(&mut self, id: usize) -> Option<&mut Dep> {
98+
if id < self.nodes.len() {
99+
return Some(&mut self.nodes[id]);
100+
}
101+
None
102+
}
103+
104+
pub fn find(&self, name: &str) -> Option<usize> {
105+
for (i, d) in self.nodes.iter().enumerate() {
106+
if d.name == name {
107+
return Some(i);
108+
}
109+
}
110+
None
111+
}
112+
65113
pub fn find_or_add(&mut self, new: &str, k: DepKind) -> usize {
66114
for (i, d) in self.nodes.iter().enumerate() {
67115
if d.name == new {
@@ -72,7 +120,8 @@ impl<'c, 'o> DepGraph<'c, 'o> {
72120
self.nodes.len() - 1
73121
}
74122

75-
pub fn render_to<W: Write>(self, output: &mut W) -> CliResult<()> {
123+
pub fn render_to<W: Write>(mut self, output: &mut W) -> CliResult<()> {
124+
self.remove_orphans();
76125
try!(writeln!(output, "{}", "digraph dependencies {"));
77126
for (i, dep) in self.nodes.iter().enumerate() {
78127
try!(write!(output, "\tN{}", i));
@@ -85,4 +134,5 @@ impl<'c, 'o> DepGraph<'c, 'o> {
85134
try!(writeln!(output, "{}", "}"));
86135
Ok(())
87136
}
137+
88138
}

src/main.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ mod fmt;
239239
mod project;
240240
mod dep;
241241
mod config;
242+
mod util;
242243

243244
static LINE_STYLES: [&'static str; 3] = ["solid", "dotted", "dashed"];
244245
static COLORS: [&'static str; 8] = ["blue", "black", "yellow", "purple", "green", "red", "white",
@@ -374,14 +375,12 @@ fn main() {
374375
if let Some(m) = m.subcommand_matches("graph") {
375376
let cfg = Config::from_matches(m).unwrap_or_else(|e| e.exit());
376377
debugln!("cfg={:#?}", cfg);
377-
if let Err(e) = execute(cfg) {
378-
e.exit();
379-
}
378+
execute(cfg).map_err(|e| e.exit() ).unwrap();
380379
}
381380
}
382381

383382
fn execute(cfg: Config) -> CliResult<()> {
384-
let project = try!(Project::from_config(&cfg));
383+
let project = try!(Project::with_config(&cfg));
385384
let graph = try!(project.graph());
386385

387386
match cfg.dot_file {

src/project.rs

Lines changed: 81 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,121 @@
1-
use std::io::Read;
2-
use std::env;
3-
use std::fs::{self, File};
4-
use std::path::{Path, PathBuf};
1+
use std::collections::HashMap;
52

6-
use toml::{self, Table, Value};
3+
use toml::Value;
74

85
use dep::{Dep, DepKind};
96
use graph::DepGraph;
107
use error::{CliErrorKind, CliResult};
118
use config::Config;
9+
use util;
10+
11+
macro_rules! propagate_kind {
12+
($me:ident, $kind:expr) => ({
13+
let mut changes = false;
14+
loop {
15+
for (dep, children) in &$me.dep_tree {
16+
if $me.styles.get(dep).unwrap() == &$kind {
17+
for child in children {
18+
if $me.styles.get(child).unwrap() != &$kind {
19+
*$me.styles.get_mut(child).unwrap() = $kind;
20+
changes = true;
21+
}
22+
}
23+
}
24+
}
25+
if !changes { break; }
26+
changes = false;
27+
}
28+
});
29+
}
1230

1331
pub struct Project<'c, 'o>
1432
where 'o: 'c
1533
{
16-
pwd: PathBuf,
1734
cfg: &'c Config<'o>,
35+
styles: HashMap<String, DepKind>,
36+
dep_tree: HashMap<String, Vec<String>>,
37+
blacklist: Vec<String>
1838
}
1939

2040
impl<'c, 'o> Project<'c, 'o> {
21-
pub fn from_config(cfg: &'c Config<'o>) -> CliResult<Self> {
22-
let pwd = try!(env::current_dir());
23-
41+
pub fn with_config(cfg: &'c Config<'o>) -> CliResult<Self> {
2442
Ok(Project {
25-
pwd: pwd,
2643
cfg: cfg,
44+
styles: HashMap::new(),
45+
dep_tree: HashMap::new(),
46+
blacklist: vec![]
2747
})
2848
}
2949

30-
fn find_project_file(&self, file: &str) -> CliResult<PathBuf> {
31-
let mut pwd = self.pwd.as_path();
50+
pub fn graph(mut self) -> CliResult<DepGraph<'c, 'o>> {
51+
let mut dg = try!(self.parse_root_deps());
3252

33-
loop {
34-
let manifest = pwd.join(file);
35-
if let Ok(metadata) = fs::metadata(&manifest) {
36-
if metadata.is_file() {
37-
return Ok(manifest);
38-
}
39-
}
53+
try!(self.parse_lock_file(&mut dg));
4054

41-
match pwd.parent() {
42-
Some(p) => pwd = p,
43-
None => break,
44-
}
45-
}
55+
self.update_styles(&mut dg);
4656

47-
Err(From::from(CliErrorKind::Generic(format!("Could not find `{}` in `{}` or any \
48-
parent directory, or it isn't a valid \
49-
lock-file",
50-
file,
51-
pwd.display()))))
57+
Ok(dg)
5258
}
5359

54-
fn toml_from_file<P: AsRef<Path>>(p: P) -> CliResult<Box<Table>> {
55-
debugln!("executing; from_file; file={:?}", p.as_ref());
56-
let mut f = try!(File::open(p.as_ref()));
57-
58-
let mut s = String::new();
59-
try!(f.read_to_string(&mut s));
60-
61-
let mut parser = toml::Parser::new(&s);
62-
match parser.parse() {
63-
Some(toml) => return Ok(Box::new(toml)),
64-
None => {}
65-
}
66-
67-
// On err
68-
let mut error_str = format!("could not parse input as TOML\n");
69-
for error in parser.errors.iter() {
70-
let (loline, locol) = parser.to_linecol(error.lo);
71-
let (hiline, hicol) = parser.to_linecol(error.hi);
72-
error_str.push_str(&format!("{:?}:{}:{}{} {}\n",
73-
f,
74-
loline + 1,
75-
locol + 1,
76-
if loline != hiline || locol != hicol {
77-
format!("-{}:{}", hiline + 1, hicol + 1)
78-
} else {
79-
"".to_owned()
80-
},
81-
error.desc));
60+
fn update_styles(&mut self, dg: &mut DepGraph<'c, 'o>) {
61+
propagate_kind!(self, DepKind::Dev);
62+
propagate_kind!(self, DepKind::Optional);
63+
propagate_kind!(self, DepKind::Build);
64+
for (name, kind) in &self.styles {
65+
if (*kind == DepKind::Dev && !self.cfg.dev_deps) ||
66+
(*kind == DepKind::Optional && !self.cfg.optional_deps) ||
67+
(*kind == DepKind::Build && !self.cfg.build_deps) {
68+
dg.remove(&*name);
69+
} else {
70+
dg.update_style(&*name, *kind);
71+
}
8272
}
83-
Err(From::from(CliErrorKind::Generic(error_str)))
73+
dg.remove_orphans();
8474
}
8575

86-
pub fn graph(mut self) -> CliResult<DepGraph<'c, 'o>> {
87-
let dg = try!(self.parse_root_deps());
88-
89-
self.parse_lock_file(dg)
90-
}
91-
92-
fn parse_lock_file(&self, mut dg: DepGraph<'c, 'o>) -> CliResult<DepGraph<'c, 'o>> {
93-
let lock_path = try!(self.find_project_file(self.cfg.lock_file));
94-
let lock_toml = try!(Project::toml_from_file(lock_path));
76+
fn parse_lock_file(&mut self, dg: &mut DepGraph<'c, 'o>) -> CliResult<()> {
77+
let lock_path = try!(util::find_manifest_file(self.cfg.lock_file));
78+
let lock_toml = try!(util::toml_from_file(lock_path));
9579

9680
if let Some(&Value::Array(ref packages)) = lock_toml.get("package") {
9781
for pkg in packages {
82+
let mut children = vec![];
9883
let name = pkg.lookup("name")
9984
.expect("no 'name' field in Cargo.lock [package] table")
10085
.as_str()
10186
.expect("'name' field of [package] table in Cargo.lock was not a \
102-
valid string");
103-
let id = dg.find_or_add(name, DepKind::Build);
87+
valid string").to_owned();
88+
let kind = match self.styles.get(&name) {
89+
Some(k) => k.clone(),
90+
None => DepKind::Build
91+
};
92+
let id = dg.find_or_add(&*name, kind);
10493

10594
if let Some(&Value::Array(ref deps)) = pkg.lookup("dependencies") {
10695
for dep in deps {
10796
let dep_string =
10897
dep.as_str().unwrap_or("").split(" ").collect::<Vec<_>>()[0];
10998

110-
dg.add_child(id, dep_string, None);
99+
children.push(dep_string.to_owned());
100+
self.styles.insert(dep_string.to_owned(), kind);
101+
dg.add_child(id, dep_string, Some(kind));
111102
}
103+
self.dep_tree.insert(name.to_owned(), children);
104+
} else {
105+
self.dep_tree.insert(name.to_owned(), vec![]);
112106
}
107+
self.styles.insert(name.to_owned(), kind);
113108
}
114109
}
115110

116111
debugln!("all lock deps: {:#?}", dg);
117-
Ok(dg)
112+
Ok(())
118113
}
119114

120115
pub fn parse_root_deps(&mut self) -> CliResult<DepGraph<'c, 'o>> {
121116
debugln!("executing; parse_root_deps;");
122-
let manifest_path = try!(self.find_project_file(self.cfg.manifest_file));
123-
let manifest_toml = try!(Project::toml_from_file(manifest_path));
117+
let manifest_path = try!(util::find_manifest_file(self.cfg.manifest_file));
118+
let manifest_toml = try!(util::toml_from_file(manifest_path));
124119
let root_table = match manifest_toml.get("package") {
125120
Some(table) => table,
126121
None => return Err(From::from(CliErrorKind::TomlTableRoot)),
@@ -140,30 +135,34 @@ impl<'c, 'o> Project<'c, 'o> {
140135

141136
if let Some(table) = manifest_toml.get("dependencies") {
142137
if let Some(table) = table.as_table() {
143-
for (name, dep_table) in table.iter() {
138+
for (name, dep_table) in table.into_iter() {
144139
if let Some(&Value::Boolean(opt)) = dep_table.lookup("optional") {
145-
if self.cfg.optional_deps && opt {
146-
dg.add_child(root_id, name, Some(DepKind::Optional));
140+
self.styles.insert(name.clone(), DepKind::Optional);
141+
dg.add_child(root_id, name, Some(DepKind::Optional));
142+
if !self.cfg.optional_deps && opt {
143+
self.blacklist.push(name.clone());
147144
}
148145
} else {
146+
self.styles.insert(name.clone(), DepKind::Build);
149147
dg.add_child(root_id, name, None);
150148
}
151149
}
152150
}
153151
}
154152

155-
if self.cfg.dev_deps {
156-
if let Some(table) = manifest_toml.get("dev-dependencies") {
157-
if let Some(table) = table.as_table() {
158-
for (name, _) in table.iter() {
159-
dg.add_child(root_id, name, Some(DepKind::Dev));
153+
if let Some(table) = manifest_toml.get("dev-dependencies") {
154+
if let Some(table) = table.as_table() {
155+
for (name, _) in table.into_iter() {
156+
self.styles.insert(name.clone(), DepKind::Dev);
157+
dg.add_child(root_id, name, Some(DepKind::Dev));
158+
if !self.cfg.dev_deps {
159+
self.blacklist.push(name.clone());
160160
}
161161
}
162162
}
163163
}
164164

165165
debugln!("root deps: {:#?}", dg);
166-
167166
Ok(dg)
168167
}
169168
}

0 commit comments

Comments
 (0)