aboutsummaryrefslogtreecommitdiff
path: root/dev_tools/src/bin/refresh-feature-mod.rs
blob: 6265e15c6d30f773f2d4a75899e9a44f6634cb96 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::collections::BTreeSet;
use std::path::Path;

use just_fmt::snake_case;
use just_template::{tmpl, Template};
use tools::println_cargo_style;

const CARGO_TOML_PATH: &str = "./mingling/Cargo.toml";
const OUTPUT_PATH: &str = "./mingling/src/features.rs";

const TEMPLATE_CONTENT: &str = include_str!("../../../mingling/src/features.rs.tmpl");

fn main() {
    gen_feature_module();
}

fn gen_feature_module() {
    let repo_root = find_git_repo().unwrap();

    let cargo_toml_path = repo_root.join(CARGO_TOML_PATH);
    let output_path = repo_root.join(OUTPUT_PATH);

    let features = parse_features(&cargo_toml_path);

    let mut template = Template::from(TEMPLATE_CONTENT);

    for feat_name in &features {
        let feat_const_name = snake_case!(feat_name).to_uppercase();

        tmpl!(template += {
            features {
                (
                    feat_name = feat_name,
                    feat_const_name = feat_const_name
                )
            }
        });
        println_cargo_style!("Refresh: feature `{}`", feat_name);
    }

    let template_str = template.to_string();
    let template_str = template_str
        .lines()
        .map(|line| line.trim_end())
        .collect::<Vec<_>>()
        .join("\n")
        + "\n";
    std::fs::write(&output_path, template_str).unwrap();

    println_cargo_style!("Written: features module to {}", OUTPUT_PATH);
}

/// Parse all feature names from the `[features]` section of a Cargo.toml.
fn parse_features(cargo_toml_path: &Path) -> Vec<String> {
    let content = std::fs::read_to_string(cargo_toml_path)
        .unwrap_or_else(|e| panic!("Failed to read {}: {}", cargo_toml_path.display(), e));

    let cargo_toml: toml::Value = content
        .parse()
        .unwrap_or_else(|e| panic!("Failed to parse {}: {}", cargo_toml_path.display(), e));

    let features_table = cargo_toml
        .get("features")
        .and_then(|v| v.as_table())
        .unwrap_or_else(|| {
            panic!(
                "No [features] section found in {}",
                cargo_toml_path.display()
            )
        });

    let mut feature_names: BTreeSet<String> = BTreeSet::new();
    for key in features_table.keys() {
        feature_names.insert(key.clone());
    }

    let mut result: Vec<String> = feature_names.into_iter().collect();
    result.sort();
    result
}

fn find_git_repo() -> Option<std::path::PathBuf> {
    let mut current_dir = std::env::current_dir().ok()?;

    loop {
        let git_dir = current_dir.join(".git");
        if git_dir.exists() && git_dir.is_dir() {
            return Some(current_dir);
        }

        if !current_dir.pop() {
            break;
        }
    }

    None
}