Skip to content

Commit

Permalink
collect defaults recursively (#28)
Browse files Browse the repository at this point in the history
fixes #19
  • Loading branch information
databasedav authored Nov 11, 2024
1 parent 4312d9b commit 5237eed
Showing 1 changed file with 65 additions and 11 deletions.
76 changes: 65 additions & 11 deletions lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ extern crate proc_macro;

use proc_macro::{TokenStream, TokenTree};
use std::borrow::Cow;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::fmt::Write;
use std::path::Path;
Expand Down Expand Up @@ -371,6 +371,35 @@ foo = bar
));
}

fn dependents(
feature_dependencies: &HashMap<String, Vec<String>>,
feature: &str,
collected: &mut HashSet<String>,
) {
if collected.contains(feature) {
return;
}
collected.insert(feature.to_string());
if let Some(dependencies) = feature_dependencies.get(feature) {
for dependency in dependencies {
dependents(feature_dependencies, dependency, collected);
}
}
}

fn parse_feature_deps<'a>(
s: &'a str,
dep: &str,
) -> Result<impl Iterator<Item = String> + 'a, String> {
Ok(s.trim()
.strip_prefix('[')
.and_then(|r| r.strip_suffix(']'))
.ok_or_else(|| format!("Parse error while parsing dependency {}", dep))?
.split(',')
.map(|d| d.trim().trim_matches(|c| c == '"' || c == '\'').trim().to_string())
.filter(|d: &String| !d.is_empty()))
}

fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
// Get all lines between the "[features]" and the next block
let mut lines = cargo_toml
Expand All @@ -385,6 +414,7 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
let mut features = vec![];
let mut default_features = HashSet::new();
let mut current_table = "";
let mut dependencies = HashMap::new();
while let Some(line) = lines.next() {
if let Some(x) = line.strip_prefix("#!") {
if !x.is_empty() && !x.starts_with(' ') {
Expand All @@ -408,6 +438,7 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
.map(|(t, _)| t.trim())
.ok_or_else(|| format!("Parse error while parsing line: {}", line))?;
if !current_comment.is_empty() {
#[allow(clippy::unnecessary_lazy_evaluations)]
let dep = current_table
.rsplit_once('.')
.and_then(|(table, dep)| table.trim().ends_with("dependencies").then(|| dep))
Expand All @@ -422,16 +453,17 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
let dep = dep.trim().trim_matches('"');
let rest = get_balanced(rest, &mut lines)
.map_err(|e| format!("Parse error while parsing value {}: {}", dep, e))?;
if current_table == "features" && dep == "default" {
let defaults = rest
.trim()
.strip_prefix('[')
.and_then(|r| r.strip_suffix(']'))
.ok_or_else(|| format!("Parse error while parsing dependency {}", dep))?
.split(',')
.map(|d| d.trim().trim_matches(|c| c == '"' || c == '\'').trim().to_string())
.filter(|d| !d.is_empty());
default_features.extend(defaults);
if current_table == "features" {
if dep == "default" {
default_features.extend(parse_feature_deps(&rest, dep)?);
} else {
for d in parse_feature_deps(&rest, dep)? {
dependencies
.entry(dep.to_string())
.or_insert_with(Vec::new)
.push(d.clone());
}
}
}
if !current_comment.is_empty() {
if current_table.ends_with("dependencies") {
Expand All @@ -456,6 +488,12 @@ fn process_toml(cargo_toml: &str, args: &Args) -> Result<String, String> {
}
}
}
let df = default_features.iter().cloned().collect::<Vec<_>>();
for feature in df {
let mut resolved = HashSet::new();
dependents(&dependencies, &feature, &mut resolved);
default_features.extend(resolved.into_iter());
}
if !current_comment.is_empty() {
return Err("Found comment not associated with a feature".into());
}
Expand Down Expand Up @@ -983,4 +1021,20 @@ default = ["teßt."]
"* <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"
);
}

#[test]
fn recursive_default() {
let toml = r#"
[features]
default=["qqq"]
## Qqq
qqq=["www"]
## Www
www=[]
"#;
let parsed = process_toml(toml, &Args::default()).unwrap();
assert_eq!(parsed, "* **`qqq`** *(enabled by default)* — Qqq\n* **`www`** *(enabled by default)* — Www\n");
}
}

0 comments on commit 5237eed

Please sign in to comment.