Feature documentation (#7814)

# Objective

- Fixes #1800, fixes #6984
- Alternative to #7196
- Ensure feature list is always up to date and that all are documented
- Help discovery of features

## Solution

- Use a template to update the cargo feature list
- Use the comment just above the feature declaration as the description
- Add the checks to CI
- Add the features to the base crate doc
This commit is contained in:
François 2023-02-28 14:24:47 +00:00
parent f732172c73
commit 261905f11d
12 changed files with 461 additions and 119 deletions

1
.github/bors.toml vendored
View file

@ -9,6 +9,7 @@ status = [
"run-examples-on-wasm",
"check-doc",
"check-missing-examples-in-docs",
"check-missing-features-in-docs",
# "check-unused-dependencies",
"ci",
"check-compiles",

View file

@ -117,7 +117,65 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: 'You added a new example but didn\'t update the readme. Please run `cargo run -p build-example-pages -- update` to update it, and commit the file change.'
body: 'You added a new example but didn\'t update the readme. Please run `cargo run -p build-templated-pages -- update examples` to update it, and commit the file change.'
});
}
missing-features:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'failure'
steps:
- name: 'Download artifact'
id: find-artifact
uses: actions/github-script@v6
with:
result-encoding: string
script: |
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
var matchArtifacts = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "missing-features"
});
if (matchArtifacts.length == 0) { return "false" }
var matchArtifact = matchArtifacts[0];
var download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
var fs = require('fs');
fs.writeFileSync('${{github.workspace}}/missing-features.zip', Buffer.from(download.data));
return "true"
- run: unzip missing-features.zip
if: ${{ steps.find-artifact.outputs.result == 'true' }}
- name: 'Comment on PR'
if: ${{ steps.find-artifact.outputs.result == 'true' }}
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
var fs = require('fs');
var issue_number = Number(fs.readFileSync('./NR'));
if (fs.existsSync('./missing-features')) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: 'You added a new feature but didn\'t add a description for it. Please update the root Cargo.toml file.'
});
}
if (fs.existsSync('./missing-update')) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: 'You added a new feature but didn\'t update the readme. Please run `cargo run -p build-templated-pages -- update features` to update it, and commit the file change.'
});
}

View file

@ -257,14 +257,14 @@ jobs:
- uses: actions/checkout@v3
- name: check for missing metadata
id: missing-metadata
run: cargo run -p build-example-pages -- check-missing
run: cargo run -p build-templated-pages -- check-missing examples
- name: check for missing update
id: missing-update
run: cargo run -p build-example-pages -- update
run: cargo run -p build-templated-pages -- update examples
- name: Check for modified files
run: |
echo "if this step fails, run the following command and commit the changed file on your PR."
echo " > cargo run -p build-example-pages -- update"
echo " > cargo run -p build-templated-pages -- update examples"
git diff --quiet HEAD --
- name: Save PR number
if: ${{ failure() && github.event_name == 'pull_request' }}
@ -283,6 +283,39 @@ jobs:
name: missing-examples
path: missing-examples/
check-missing-features-in-docs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- name: check for missing features
id: missing-features
run: cargo run -p build-templated-pages -- check-missing features
- name: check for missing update
id: missing-update
run: cargo run -p build-templated-pages -- update features
- name: Check for modified files
run: |
echo "if this step fails, run the following command and commit the changed file on your PR."
echo " > cargo run -p build-templated-pages -- update features"
git diff --quiet HEAD --
- name: Save PR number
if: ${{ failure() && github.event_name == 'pull_request' }}
run: |
mkdir -p ./missing-features
echo ${{ github.event.number }} > ./missing-features/NR
- name: log failed task - missing features
if: ${{ failure() && github.event_name == 'pull_request' && steps.missing-features.conclusion == 'failure' }}
run: touch ./missing-features/missing-features
- name: log failed task - missing update
if: ${{ failure() && github.event_name == 'pull_request' && steps.missing-update.conclusion == 'failure' }}
run: touch ./missing-features/missing-update
- uses: actions/upload-artifact@v2
if: ${{ failure() && github.event_name == 'pull_request' }}
with:
name: missing-features
path: missing-features/
check-unused-dependencies:
runs-on: ubuntu-latest
timeout-minutes: 30

View file

@ -22,7 +22,7 @@ members = [
"crates/*",
"examples/mobile",
"tools/ci",
"tools/build-example-pages",
"tools/build-templated-pages",
"tools/build-wasm-example",
"errors",
]
@ -56,62 +56,136 @@ default = [
# Force dynamic linking, which improves iterative compile times
dynamic_linking = ["bevy_dylib", "bevy_internal/dynamic_linking"]
# Optional bevy crates
# Provides animation functionality
bevy_animation = ["bevy_internal/bevy_animation"]
# Provides asset functionality
bevy_asset = ["bevy_internal/bevy_asset"]
# Provides audio functionality
bevy_audio = ["bevy_internal/bevy_audio"]
# Provides cameras and other basic render pipeline features
bevy_core_pipeline = ["bevy_internal/bevy_core_pipeline"]
# Plugin for dynamic loading (using [libloading](https://crates.io/crates/libloading))
bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"]
# Adds gamepad support
bevy_gilrs = ["bevy_internal/bevy_gilrs"]
# [glTF](https://www.khronos.org/gltf/) support
bevy_gltf = ["bevy_internal/bevy_gltf"]
# Adds PBR rendering
bevy_pbr = ["bevy_internal/bevy_pbr"]
# Provides rendering functionality
bevy_render = ["bevy_internal/bevy_render"]
# Provides scene functionality
bevy_scene = ["bevy_internal/bevy_scene"]
# Provides sprite functionality
bevy_sprite = ["bevy_internal/bevy_sprite"]
# Provides text functionality
bevy_text = ["bevy_internal/bevy_text"]
# A custom ECS-driven UI framework
bevy_ui = ["bevy_internal/bevy_ui"]
# winit window and input backend
bevy_winit = ["bevy_internal/bevy_winit"]
# Tracing features
# Tracing support, saving a file in Chrome Tracing format
trace_chrome = ["trace", "bevy_internal/trace_chrome"]
# Tracing support, exposing a port for Tracy
trace_tracy = ["trace", "bevy_internal/trace_tracy"]
# Tracing support
trace = ["bevy_internal/trace"]
# Save a trace of all wgpu calls
wgpu_trace = ["bevy_internal/wgpu_trace"]
# Image format support for texture loading (PNG and HDR are enabled by default)
# EXR image format support
exr = ["bevy_internal/exr"]
# HDR image format support
hdr = ["bevy_internal/hdr"]
# PNG image format support
png = ["bevy_internal/png"]
# TGA image format support
tga = ["bevy_internal/tga"]
# JPEG image format support
jpeg = ["bevy_internal/jpeg"]
# BMP image format support
bmp = ["bevy_internal/bmp"]
# Basis Universal compressed texture support
basis-universal = ["bevy_internal/basis-universal"]
# DDS compressed texture support
dds = ["bevy_internal/dds"]
# KTX2 compressed texture support
ktx2 = ["bevy_internal/ktx2"]
# For ktx2 supercompression
# For KTX2 supercompression
zlib = ["bevy_internal/zlib"]
# For KTX2 supercompression
zstd = ["bevy_internal/zstd"]
# Audio format support (vorbis is enabled by default)
# FLAC audio format support
flac = ["bevy_internal/flac"]
# MP3 audio format support
mp3 = ["bevy_internal/mp3"]
# OGG/VORBIS audio format support
vorbis = ["bevy_internal/vorbis"]
# WAV audio format support
wav = ["bevy_internal/wav"]
# AAC audio format support (through symphonia)
symphonia-aac = ["bevy_internal/symphonia-aac"]
# AAC, FLAC, MP3, MP4, OGG/VORBIS, and WAV audio formats support (through symphonia)
symphonia-all = ["bevy_internal/symphonia-all"]
# FLAC audio format support (through symphonia)
symphonia-flac = ["bevy_internal/symphonia-flac"]
# MP4 audio format support (through symphonia)
symphonia-isomp4 = ["bevy_internal/symphonia-isomp4"]
# MP3 audio format support (through symphonia)
symphonia-mp3 = ["bevy_internal/symphonia-mp3"]
# OGG/VORBIS audio format support (through symphonia)
symphonia-vorbis = ["bevy_internal/symphonia-vorbis"]
# WAV audio format support (through symphonia)
symphonia-wav = ["bevy_internal/symphonia-wav"]
# Enable watching file system for asset hot reload
filesystem_watcher = ["bevy_internal/filesystem_watcher"]
# Enable serialization support through serde
serialize = ["bevy_internal/serialize"]
# Display server protocol support (X11 is enabled by default)
# Wayland display server support
wayland = ["bevy_internal/wayland"]
# X11 display server support
x11 = ["bevy_internal/x11"]
# Enable rendering of font glyphs using subpixel accuracy
@ -124,16 +198,15 @@ bevy_ci_testing = ["bevy_internal/bevy_ci_testing"]
debug_asset_server = ["bevy_internal/debug_asset_server"]
# Enable animation support, and glTF animation loading
animation = ["bevy_internal/animation"]
animation = ["bevy_internal/animation", "bevy_animation"]
# Enable using a shared stdlib for cxx on Android.
# Enable using a shared stdlib for cxx on Android
android_shared_stdcxx = ["bevy_internal/android_shared_stdcxx"]
# Enable detailed trace event logging.
# These trace events are expensive even when off, thus they require compile time opt-in.
# Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in
detailed_trace = ["bevy_internal/detailed_trace"]
# Include tonemapping LUT KTX2 files.
# Include tonemapping Look Up Tables KTX2 files
tonemapping_luts = ["bevy_internal/tonemapping_luts"]
[dependencies]
@ -1267,7 +1340,7 @@ category = "Scene"
wasm = false
# Shaders
[[package.metadata.category]]
[[package.metadata.example_category]]
name = "Shaders"
description = """
These examples demonstrate how to implement different shaders in user code.
@ -1399,7 +1472,7 @@ category = "Shaders"
wasm = false
# Stress tests
[[package.metadata.category]]
[[package.metadata.example_category]]
name = "Stress Tests"
description = """
These examples are used to test the performance and stability of various parts of the engine in an isolated way.

View file

@ -0,0 +1,21 @@
<!-- MD041 - This file will be included in docs and should not start with a top header -->
<!-- markdownlint-disable-file MD041 -->
## Cargo Features
Bevy exposes many features to customise the engine. Enabling them add functionalities but often come at the cost of longer compilation times and extra dependencies.
### Default Features
The default feature set enables most of the expected features of a game engine, like rendering in both 2D and 3D, asset loading, audio and UI. To help reduce compilation time, consider disabling default features and enabling only those you need.
|feature name|description|
|-|-|
{% for feature in features %}{% if feature.is_default %}|{{ feature.name }}|{{ feature.description }}|
{% endif %}{% endfor %}
### Optional Features
|feature name|description|
|-|-|
{% for feature in features %}{% if feature.is_default == False %}|{{ feature.name }}|{{ feature.description }}|
{% endif %}{% endfor %}

View file

@ -1,53 +1,70 @@
# Cargo Features
<!-- MD041 - This file will be included in docs and should not start with a top header -->
<!-- markdownlint-disable-file MD041 -->
## Default Features
## Cargo Features
Bevy exposes many features to customise the engine. Enabling them add functionalities but often come at the cost of longer compilation times and extra dependencies.
### Default Features
The default feature set enables most of the expected features of a game engine, like rendering in both 2D and 3D, asset loading, audio and UI. To help reduce compilation time, consider disabling default features and enabling only those you need.
|feature name|description|
|-|-|
|animation|Animation support and glTF animation loading.|
|bevy_asset|Provides asset functionality for Bevy Engine.|
|bevy_audio|Audio support. Support for all audio formats depends on this.|
|bevy_gilrs|Adds gamepad support.|
|bevy_gltf|[glTF](https://www.khronos.org/gltf/) support.|
|bevy_scene|Provides scene functionality for Bevy Engine.|
|bevy_winit|GUI support.|
|render|The render pipeline and all render related plugins.|
|png|PNG picture format support.|
|hdr|[HDR](https://en.wikipedia.org/wiki/High_dynamic_range) support.|
|vorbis|Ogg Vorbis audio format support.|
|x11|Make GUI applications use X11 protocol. You could enable wayland feature to override this.|
|filesystem_watcher|Enable watching the file system for asset hot reload|
|android_shared_stdcxx|Enable using a shared stdlib for cxx on Android|
|animation|Enable animation support, and glTF animation loading|
|bevy_animation|Provides animation functionality|
|bevy_asset|Provides asset functionality|
|bevy_audio|Provides audio functionality|
|bevy_core_pipeline|Provides cameras and other basic render pipeline features|
|bevy_gilrs|Adds gamepad support|
|bevy_gltf|[glTF](https://www.khronos.org/gltf/) support|
|bevy_pbr|Adds PBR rendering|
|bevy_render|Provides rendering functionality|
|bevy_scene|Provides scene functionality|
|bevy_sprite|Provides sprite functionality|
|bevy_text|Provides text functionality|
|bevy_ui|A custom ECS-driven UI framework|
|bevy_winit|winit window and input backend|
|filesystem_watcher|Enable watching file system for asset hot reload|
|hdr|HDR image format support|
|ktx2|KTX2 compressed texture support|
|png|PNG image format support|
|tonemapping_luts|Include tonemapping Look Up Tables KTX2 files|
|vorbis|OGG/VORBIS audio format support|
|x11|X11 display server support|
|zstd|For KTX2 supercompression|
## Optional Features
### Optional Features
|feature name|description|
|-|-|
|bevy_dynamic_plugin|Plugin for dynamic loading (using [libloading](https://crates.io/crates/libloading)).|
|dynamic_linking|Forces bevy to be dynamically linked, which improves iterative compile times.|
|trace|Enables system tracing.|
|trace_chrome|Enables [tracing-chrome](https://github.com/thoren-d/tracing-chrome) as bevy_log output. This allows you to visualize system execution.|
|trace_tracy|Enables [Tracy](https://github.com/wolfpld/tracy) as bevy_log output. This allows `Tracy` to connect to and capture profiling data as well as visualize system execution in real-time, present statistics about system execution times, and more.|
|wgpu_trace|For tracing wgpu.|
|dds|DDS picture format support.|
|ktx2|KTX2 picture format support.|
|zlib|KTX2 Zlib supercompression support.|
|zstd|KTX2 Zstandard supercompression support.|
|basis-universal|Basis Universal picture format support and, if the `ktx2` feature is enabled, also KTX2 UASTC picture format transcoding support.|
|tga|TGA picture format support.|
|jpeg|JPEG picture format support.|
|bmp|BMP picture format support.|
|flac|FLAC audio format support. It's included in bevy_audio feature.|
|mp3|MP3 audio format support.|
|wav|WAV audio format support.|
|symphonia-aac|AAC audio format support by Symphonia. For more details, see `symphonia-all`.|
|symphonia-all|AAC, FLAC, MP4, MP3, Vorbis, and WAV support by Symphonia. Add support for parsing multiple file formats using a single crate instead of compiling different crates. The other non-`symphonia` features are disabled when its corresponding `symphonia` feature is enabled. [Link to `symphonia` documentation](https://docs.rs/symphonia/latest/symphonia/). More information about this topic can be found [here](https://github.com/bevyengine/bevy/pull/6388#discussion_r1009622883) |
|symphonia-flac|FLAC audio format support by Symphonia. For more details, see `symphonia-all`.|
|symphonia-isomp4|MP4 audio format support by Symphonia. For more details, see `symphonia-all`.|
|symphonia-mp3|MP3 audio format support by Symphonia. For more details, see `symphonia-all`.|
|symphonia-vorbis|Vorbis audio format support by Symphonia. For more details, see `symphonia-all`.|
|symphonia-wav|WAV audio format support by Symphonia. For more details, see `symphonia-all`.|
|serialize|Enables serialization of `bevy_input` types.|
|wayland|Enable this to use Wayland display server protocol other than X11.|
|subpixel_glyph_atlas|Enable this to cache glyphs using subpixel accuracy. This increases texture memory usage as each position requires a separate sprite in the glyph atlas, but provide more accurate character spacing.|
|bevy_ci_testing|Used for running examples in CI.|
|debug_asset_server|Enabling this turns on "hot reloading" of built in assets, such as shaders.|
|basis-universal|Basis Universal compressed texture support|
|bevy_ci_testing|Enable systems that allow for automated testing on CI|
|bevy_dynamic_plugin|Plugin for dynamic loading (using [libloading](https://crates.io/crates/libloading))|
|bmp|BMP image format support|
|dds|DDS compressed texture support|
|debug_asset_server|Enable the "debug asset server" for hot reloading internal assets|
|detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in|
|dynamic_linking|Force dynamic linking, which improves iterative compile times|
|exr|EXR image format support|
|flac|FLAC audio format support|
|jpeg|JPEG image format support|
|mp3|MP3 audio format support|
|serialize|Enable serialization support through serde|
|subpixel_glyph_atlas|Enable rendering of font glyphs using subpixel accuracy|
|symphonia-aac|AAC audio format support (through symphonia)|
|symphonia-all|AAC, FLAC, MP3, MP4, OGG/VORBIS, and WAV audio formats support (through symphonia)|
|symphonia-flac|FLAC audio format support (through symphonia)|
|symphonia-isomp4|MP4 audio format support (through symphonia)|
|symphonia-mp3|MP3 audio format support (through symphonia)|
|symphonia-vorbis|OGG/VORBIS audio format support (through symphonia)|
|symphonia-wav|WAV audio format support (through symphonia)|
|tga|TGA image format support|
|trace|Tracing support|
|trace_chrome|Tracing support, saving a file in Chrome Tracing format|
|trace_tracy|Tracing support, exposing a port for Tracy|
|wav|WAV audio format support|
|wayland|Wayland display server support|
|wgpu_trace|Save a trace of all wgpu calls|
|zlib|For KTX2 supercompression|

View file

@ -10,6 +10,7 @@
//! community](https://bevyengine.org/community/) if you have any questions or ideas!
//!
//! ## Example
//!
//! Here is a simple "Hello World" Bevy app:
//! ```
//! use bevy::prelude::*;
@ -28,7 +29,8 @@
//! Don't let the simplicity of the example above fool you. Bevy is a [fully featured game engine](https://bevyengine.org)
//! and it gets more powerful every day!
//!
//! ### This Crate
//! ## This Crate
//!
//! The `bevy` crate is just a container crate that makes it easier to consume Bevy subcrates.
//! The defaults provide a "full" engine experience, but you can easily enable / disable features
//! in your project's `Cargo.toml` to meet your specific needs. See Bevy's `Cargo.toml` for a full
@ -37,7 +39,7 @@
//! If you prefer, you can also consume the individual bevy crates directly.
//! Each module in the root of this crate, except for the prelude, can be found on crates.io
//! with `bevy_` appended to the front, e.g. `app` -> [`bevy_app`](https://docs.rs/bevy_app/*/bevy_app/).
#![doc = include_str!("../docs/cargo_features.md")]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"

View file

@ -1,8 +1,8 @@
[package]
name = "build-example-pages"
name = "build-templated-pages"
version = "0.9.0"
edition = "2021"
description = "handle examples in Bevy"
description = "handle templated pages in Bevy repository"
publish = false
license = "MIT OR Apache-2.0"

View file

@ -1,60 +1,10 @@
use std::{cmp::Ordering, collections::HashMap, fs::File};
use bitflags::bitflags;
use serde::Serialize;
use tera::{Context, Tera};
use toml_edit::Document;
bitflags! {
struct Command: u32 {
const CHECK_MISSING = 0b00000001;
const UPDATE = 0b00000010;
}
}
fn main() {
let what_to_run = match std::env::args().nth(1).as_deref() {
Some("check-missing") => Command::CHECK_MISSING,
Some("update") => Command::UPDATE,
_ => Command::all(),
};
let examples = parse_examples(what_to_run.contains(Command::CHECK_MISSING));
if what_to_run.contains(Command::UPDATE) {
let categories = parse_categories();
let examples_by_category: HashMap<String, Category> = examples
.into_iter()
.fold(HashMap::<String, Vec<Example>>::new(), |mut v, ex| {
v.entry(ex.category.clone()).or_default().push(ex);
v
})
.into_iter()
.map(|(key, mut examples)| {
examples.sort();
let description = categories.get(&key).cloned();
(
key,
Category {
description,
examples,
},
)
})
.collect();
let mut context = Context::new();
context.insert("all_examples", &examples_by_category);
Tera::new("examples/*.md.tpl")
.expect("error parsing template")
.render_to(
"README.md.tpl",
&context,
File::create("examples/README.md").expect("error creating file"),
)
.expect("error rendering template");
}
}
use crate::Command;
#[derive(Debug, Serialize, PartialEq, Eq)]
struct Category {
@ -138,7 +88,7 @@ fn parse_categories() -> HashMap<String, String> {
.unwrap()
.get("metadata")
.as_ref()
.unwrap()["category"]
.unwrap()["example_category"]
.clone()
.as_array_of_tables()
.unwrap()
@ -151,3 +101,41 @@ fn parse_categories() -> HashMap<String, String> {
})
.collect()
}
pub(crate) fn check(what_to_run: Command) {
let examples = parse_examples(what_to_run.contains(Command::CHECK_MISSING));
if what_to_run.contains(Command::UPDATE) {
let categories = parse_categories();
let examples_by_category: HashMap<String, Category> = examples
.into_iter()
.fold(HashMap::<String, Vec<Example>>::new(), |mut v, ex| {
v.entry(ex.category.clone()).or_default().push(ex);
v
})
.into_iter()
.map(|(key, mut examples)| {
examples.sort();
let description = categories.get(&key).cloned();
(
key,
Category {
description,
examples,
},
)
})
.collect();
let mut context = Context::new();
context.insert("all_examples", &examples_by_category);
Tera::new("docs-template/*.md.tpl")
.expect("error parsing template")
.render_to(
"EXAMPLE_README.md.tpl",
&context,
File::create("examples/README.md").expect("error creating file"),
)
.expect("error rendering template");
}
}

View file

@ -0,0 +1,110 @@
use std::{cmp::Ordering, fs::File};
use serde::Serialize;
use tera::{Context, Tera};
use toml_edit::Document;
use crate::Command;
#[derive(Debug, Serialize, PartialEq, Eq)]
struct Feature {
name: String,
description: String,
is_default: bool,
}
impl Ord for Feature {
fn cmp(&self, other: &Self) -> Ordering {
self.name.cmp(&other.name)
}
}
impl PartialOrd for Feature {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
fn parse_features(panic_on_missing: bool) -> Vec<Feature> {
let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap();
let manifest = manifest_file.parse::<Document>().unwrap();
let features = manifest["features"].as_table().unwrap();
let default: Vec<_> = features
.get("default")
.unwrap()
.as_array()
.unwrap()
.iter()
.flat_map(|v| {
std::iter::once(v.as_str().unwrap().to_string()).chain(
features
.get(v.as_str().unwrap())
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|v| v.as_str().unwrap().to_string()),
)
})
.collect();
features
.get_values()
.iter()
.flat_map(|(key, _)| {
let key = key[0];
if key == "default" {
None
} else {
let name = key
.as_repr()
.unwrap()
.as_raw()
.as_str()
.unwrap()
.to_string();
if let Some(description) = key.decor().prefix() {
let description = description.as_str().unwrap().to_string();
if !description.starts_with("\n# ") || !description.ends_with('\n') {
panic!("Missing description for feature {name}");
}
let description = description
.strip_prefix("\n# ")
.unwrap()
.strip_suffix('\n')
.unwrap()
.to_string();
Some(Feature {
is_default: default.contains(&name),
name,
description,
})
} else if panic_on_missing {
panic!("Missing description for feature {name}");
} else {
None
}
}
})
.collect()
}
pub(crate) fn check(what_to_run: Command) {
let mut features = parse_features(what_to_run.contains(Command::CHECK_MISSING));
features.sort();
if what_to_run.contains(Command::UPDATE) {
let mut context = Context::new();
context.insert("features", &features);
Tera::new("docs-template/*.md.tpl")
.expect("error parsing template")
.render_to(
"features.md.tpl",
&context,
File::create("docs/cargo_features.md").expect("error creating file"),
)
.expect("error rendering template");
}
}

View file

@ -0,0 +1,39 @@
use bitflags::bitflags;
mod examples;
mod features;
bitflags! {
struct Command: u32 {
const CHECK_MISSING = 0b00000001;
const UPDATE = 0b00000010;
}
}
bitflags! {
struct Target: u32 {
const EXAMPLES = 0b00000001;
const FEATURES = 0b00000010;
}
}
fn main() {
let what_to_run = match std::env::args().nth(1).as_deref() {
Some("check-missing") => Command::CHECK_MISSING,
Some("update") => Command::UPDATE,
_ => Command::all(),
};
let target = match std::env::args().nth(2).as_deref() {
Some("examples") => Target::EXAMPLES,
Some("features") => Target::FEATURES,
_ => Target::all(),
};
if target.contains(Target::EXAMPLES) {
examples::check(what_to_run);
}
if target.contains(Target::FEATURES) {
features::check(what_to_run);
}
}