Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/tools/build-templated-pages/src/features.rs
9358 views
1
use core::cmp::Ordering;
2
use std::fs::File;
3
4
use serde::Serialize;
5
use tera::{Context, Tera};
6
use toml_edit::DocumentMut;
7
8
use crate::Command;
9
10
#[derive(Debug, Serialize, PartialEq, Eq, Clone)]
11
struct Feature {
12
name: String,
13
description: String,
14
is_profile: bool,
15
is_collection: bool,
16
}
17
18
impl Ord for Feature {
19
fn cmp(&self, other: &Self) -> Ordering {
20
self.name.cmp(&other.name)
21
}
22
}
23
24
impl PartialOrd for Feature {
25
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
26
Some(self.cmp(other))
27
}
28
}
29
30
fn parse_features(panic_on_missing: bool) -> Vec<Feature> {
31
let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap();
32
let manifest = manifest_file.parse::<DocumentMut>().unwrap();
33
34
let features = manifest["features"].as_table().unwrap();
35
36
features
37
.get_values()
38
.iter()
39
.flat_map(|(key, value)| {
40
let key = key[0];
41
42
if key == "default" {
43
let values = value
44
.as_array()
45
.unwrap()
46
.iter()
47
.flat_map(|v| v.as_str())
48
.collect::<Vec<_>>()
49
.join(", ");
50
let description = format!("The full default Bevy experience. This is a combination of the following profiles: {values}");
51
52
Some(Feature {
53
is_profile: true,
54
is_collection: false,
55
name: "default".to_string(),
56
description,
57
})
58
} else {
59
let name = key
60
.as_repr()
61
.unwrap()
62
.as_raw()
63
.as_str()
64
.unwrap()
65
.to_string();
66
if let Some(description) = key.leaf_decor().prefix() {
67
let description = description.as_str().unwrap().to_string();
68
if !description.starts_with("\n# ") || !description.ends_with('\n') {
69
panic!("Missing description for feature {name}");
70
}
71
let mut description = description
72
.strip_prefix("\n# ")
73
.unwrap()
74
.strip_suffix('\n')
75
.unwrap()
76
.to_string();
77
let is_profile = if let Some(trimmed) = description.strip_prefix("PROFILE: ") {
78
description = trimmed.to_string();
79
true
80
} else {
81
false
82
};
83
let is_collection =
84
if let Some(trimmed) = description.strip_prefix("COLLECTION: ") {
85
description = trimmed.to_string();
86
true
87
} else {
88
false
89
};
90
91
if is_collection {
92
let features = value
93
.as_array()
94
.unwrap()
95
.iter()
96
.flat_map(|v| v.as_str().map(|s| format!("`{}`", s)))
97
.collect::<Vec<_>>()
98
.join(", ");
99
description.push_str(&format!(" **Feature set:** {}.", &features));
100
}
101
102
Some(Feature {
103
is_profile,
104
is_collection,
105
name,
106
description,
107
})
108
} else if panic_on_missing {
109
panic!("Missing description for feature {name}");
110
} else {
111
None
112
}
113
}
114
})
115
.collect()
116
}
117
118
pub(crate) fn check(what_to_run: Command) {
119
let features = parse_features(what_to_run.contains(Command::CHECK_MISSING));
120
let mut sorted_features = features.clone();
121
sorted_features.sort();
122
123
if what_to_run.contains(Command::UPDATE) {
124
let mut context = Context::new();
125
context.insert("features", &features);
126
context.insert("sorted_features", &sorted_features);
127
Tera::new("docs-template/*.md.tpl")
128
.expect("error parsing template")
129
.render_to(
130
"features.md.tpl",
131
&context,
132
File::create("docs/cargo_features.md").expect("error creating file"),
133
)
134
.expect("error rendering template");
135
}
136
}
137
138