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
|
//! Regenerates `mingling/src/features.rs` from the `[features]` section of
//! `mingling/Cargo.toml`.
use std::collections::HashMap;
use std::fs;
use just_fmt::snake_case;
use just_template::Template;
use mingling::{
Grouped, RenderResult, Routable,
macros::{buffer, command, r_println, renderer},
};
use crate::Next;
use crate::res::{CargoError, MessagePrinter};
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");
#[command(node = "features-refresh")]
pub fn features_refresh() -> Next {
match gen_feature_module() {
Ok(written) => ResultFeaturesRefresh { written }.to_chain(),
Err(e) => ErrorFeaturesRefresh(e).to_chain(),
}
}
fn gen_feature_module() -> Result<Vec<String>, String> {
let features = parse_features()?;
let mut template = Template::from(TEMPLATE_CONTENT);
let mut written = Vec::new();
for feat_name in &features {
let feat_const_name = snake_case!(feat_name).to_uppercase();
template
.add_impl("features".to_string())
.push(HashMap::from([
("feat_name".to_string(), feat_name.clone()),
("feat_const_name".to_string(), feat_const_name),
]));
written.push(format!("feature: {feat_name}"));
}
let template_str = template.to_string();
let template_str = template_str
.lines()
.map(str::trim_end)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
fs::write(OUTPUT_PATH, template_str)
.map_err(|e| format!("failed to write {OUTPUT_PATH}: {e}"))?;
written.push(format!("written: {OUTPUT_PATH}"));
Ok(written)
}
/// All feature names from the `[features]` section, sorted.
fn parse_features() -> Result<Vec<String>, String> {
let content = fs::read_to_string(CARGO_TOML_PATH)
.map_err(|e| format!("failed to read {CARGO_TOML_PATH}: {e}"))?;
let table: toml::Value = content
.parse()
.map_err(|e| format!("failed to parse {CARGO_TOML_PATH}: {e}"))?;
let features = table
.get("features")
.and_then(|v| v.as_table())
.ok_or_else(|| format!("no [features] section in {CARGO_TOML_PATH}"))?;
let mut names: Vec<String> = features.keys().cloned().collect();
names.sort();
Ok(names)
}
/// Feature names written by `features-refresh`.
#[derive(Grouped)]
pub struct ResultFeaturesRefresh {
pub written: Vec<String>,
}
#[derive(Grouped, Default)]
pub struct ErrorFeaturesRefresh(pub String);
#[renderer(buffer)]
pub fn render_features_refresh(r: ResultFeaturesRefresh) {
for item in r.written {
r_println!("{item}");
}
}
#[renderer]
pub fn render_error_features_refresh(e: ErrorFeaturesRefresh, error: &CargoError) -> RenderResult {
let render_result = RenderResult::new();
error.println(vec![e.0]);
render_result
}
|