Skip to content

Commit 5237eed

Browse files
authored
collect defaults recursively (#28)
fixes #19
1 parent 4312d9b commit 5237eed

File tree

1 file changed

+65
-11
lines changed

1 file changed

+65
-11
lines changed

lib.rs

+65-11
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ extern crate proc_macro;
154154

155155
use proc_macro::{TokenStream, TokenTree};
156156
use std::borrow::Cow;
157-
use std::collections::HashSet;
157+
use std::collections::{HashMap, HashSet};
158158
use std::convert::TryFrom;
159159
use std::fmt::Write;
160160
use std::path::Path;
@@ -371,6 +371,35 @@ foo = bar
371371
));
372372
}
373373

374+
fn dependents(
375+
feature_dependencies: &HashMap<String, Vec<String>>,
376+
feature: &str,
377+
collected: &mut HashSet<String>,
378+
) {
379+
if collected.contains(feature) {
380+
return;
381+
}
382+
collected.insert(feature.to_string());
383+
if let Some(dependencies) = feature_dependencies.get(feature) {
384+
for dependency in dependencies {
385+
dependents(feature_dependencies, dependency, collected);
386+
}
387+
}
388+
}
389+
390+
fn parse_feature_deps<'a>(
391+
s: &'a str,
392+
dep: &str,
393+
) -> Result<impl Iterator<Item = String> + 'a, String> {
394+
Ok(s.trim()
395+
.strip_prefix('[')
396+
.and_then(|r| r.strip_suffix(']'))
397+
.ok_or_else(|| format!("Parse error while parsing dependency {}", dep))?
398+
.split(',')
399+
.map(|d| d.trim().trim_matches(|c| c == '"' || c == '\'').trim().to_string())
400+
.filter(|d: &String| !d.is_empty()))
401+
}
402+
374403
fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
375404
// Get all lines between the "[features]" and the next block
376405
let mut lines = cargo_toml
@@ -385,6 +414,7 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
385414
let mut features = vec![];
386415
let mut default_features = HashSet::new();
387416
let mut current_table = "";
417+
let mut dependencies = HashMap::new();
388418
while let Some(line) = lines.next() {
389419
if let Some(x) = line.strip_prefix("#!") {
390420
if !x.is_empty() && !x.starts_with(' ') {
@@ -408,6 +438,7 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
408438
.map(|(t, _)| t.trim())
409439
.ok_or_else(|| format!("Parse error while parsing line: {}", line))?;
410440
if !current_comment.is_empty() {
441+
#[allow(clippy::unnecessary_lazy_evaluations)]
411442
let dep = current_table
412443
.rsplit_once('.')
413444
.and_then(|(table, dep)| table.trim().ends_with("dependencies").then(|| dep))
@@ -422,16 +453,17 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
422453
let dep = dep.trim().trim_matches('"');
423454
let rest = get_balanced(rest, &mut lines)
424455
.map_err(|e| format!("Parse error while parsing value {}: {}", dep, e))?;
425-
if current_table == "features" && dep == "default" {
426-
let defaults = rest
427-
.trim()
428-
.strip_prefix('[')
429-
.and_then(|r| r.strip_suffix(']'))
430-
.ok_or_else(|| format!("Parse error while parsing dependency {}", dep))?
431-
.split(',')
432-
.map(|d| d.trim().trim_matches(|c| c == '"' || c == '\'').trim().to_string())
433-
.filter(|d| !d.is_empty());
434-
default_features.extend(defaults);
456+
if current_table == "features" {
457+
if dep == "default" {
458+
default_features.extend(parse_feature_deps(&rest, dep)?);
459+
} else {
460+
for d in parse_feature_deps(&rest, dep)? {
461+
dependencies
462+
.entry(dep.to_string())
463+
.or_insert_with(Vec::new)
464+
.push(d.clone());
465+
}
466+
}
435467
}
436468
if !current_comment.is_empty() {
437469
if current_table.ends_with("dependencies") {
@@ -456,6 +488,12 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
456488
}
457489
}
458490
}
491+
let df = default_features.iter().cloned().collect::<Vec<_>>();
492+
for feature in df {
493+
let mut resolved = HashSet::new();
494+
dependents(&dependencies, &feature, &mut resolved);
495+
default_features.extend(resolved.into_iter());
496+
}
459497
if !current_comment.is_empty() {
460498
return Err("Found comment not associated with a feature".into());
461499
}
@@ -983,4 +1021,20 @@ default = ["teßt."]
9831021
"* <span class=\"stab portability\"><code>teßt.</code></span> *(enabled by default)* — This is a test\n* <span class=\"stab portability\"><code>dep</code></span> — A dep\n"
9841022
);
9851023
}
1024+
1025+
#[test]
1026+
fn recursive_default() {
1027+
let toml = r#"
1028+
[features]
1029+
default=["qqq"]
1030+
1031+
## Qqq
1032+
qqq=["www"]
1033+
1034+
## Www
1035+
www=[]
1036+
"#;
1037+
let parsed = process_toml(toml, &Args::default()).unwrap();
1038+
assert_eq!(parsed, "* **`qqq`** *(enabled by default)* — Qqq\n* **`www`** *(enabled by default)* — Www\n");
1039+
}
9861040
}

0 commit comments

Comments
 (0)