Path: blob/main/tools/build-templated-pages/src/features.rs
9358 views
use core::cmp::Ordering;1use std::fs::File;23use serde::Serialize;4use tera::{Context, Tera};5use toml_edit::DocumentMut;67use crate::Command;89#[derive(Debug, Serialize, PartialEq, Eq, Clone)]10struct Feature {11name: String,12description: String,13is_profile: bool,14is_collection: bool,15}1617impl Ord for Feature {18fn cmp(&self, other: &Self) -> Ordering {19self.name.cmp(&other.name)20}21}2223impl PartialOrd for Feature {24fn partial_cmp(&self, other: &Self) -> Option<Ordering> {25Some(self.cmp(other))26}27}2829fn parse_features(panic_on_missing: bool) -> Vec<Feature> {30let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap();31let manifest = manifest_file.parse::<DocumentMut>().unwrap();3233let features = manifest["features"].as_table().unwrap();3435features36.get_values()37.iter()38.flat_map(|(key, value)| {39let key = key[0];4041if key == "default" {42let values = value43.as_array()44.unwrap()45.iter()46.flat_map(|v| v.as_str())47.collect::<Vec<_>>()48.join(", ");49let description = format!("The full default Bevy experience. This is a combination of the following profiles: {values}");5051Some(Feature {52is_profile: true,53is_collection: false,54name: "default".to_string(),55description,56})57} else {58let name = key59.as_repr()60.unwrap()61.as_raw()62.as_str()63.unwrap()64.to_string();65if let Some(description) = key.leaf_decor().prefix() {66let description = description.as_str().unwrap().to_string();67if !description.starts_with("\n# ") || !description.ends_with('\n') {68panic!("Missing description for feature {name}");69}70let mut description = description71.strip_prefix("\n# ")72.unwrap()73.strip_suffix('\n')74.unwrap()75.to_string();76let is_profile = if let Some(trimmed) = description.strip_prefix("PROFILE: ") {77description = trimmed.to_string();78true79} else {80false81};82let is_collection =83if let Some(trimmed) = description.strip_prefix("COLLECTION: ") {84description = trimmed.to_string();85true86} else {87false88};8990if is_collection {91let features = value92.as_array()93.unwrap()94.iter()95.flat_map(|v| v.as_str().map(|s| format!("`{}`", s)))96.collect::<Vec<_>>()97.join(", ");98description.push_str(&format!(" **Feature set:** {}.", &features));99}100101Some(Feature {102is_profile,103is_collection,104name,105description,106})107} else if panic_on_missing {108panic!("Missing description for feature {name}");109} else {110None111}112}113})114.collect()115}116117pub(crate) fn check(what_to_run: Command) {118let features = parse_features(what_to_run.contains(Command::CHECK_MISSING));119let mut sorted_features = features.clone();120sorted_features.sort();121122if what_to_run.contains(Command::UPDATE) {123let mut context = Context::new();124context.insert("features", &features);125context.insert("sorted_features", &sorted_features);126Tera::new("docs-template/*.md.tpl")127.expect("error parsing template")128.render_to(129"features.md.tpl",130&context,131File::create("docs/cargo_features.md").expect("error creating file"),132)133.expect("error rendering template");134}135}136137138