diff --git a/.github/bors.toml b/.github/bors.toml index 087a395e95..983d58c68b 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -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", diff --git a/.github/workflows/ci-comment-failures.yml b/.github/workflows/ci-comment-failures.yml index c5bf3007ed..d8f5174c53 100644 --- a/.github/workflows/ci-comment-failures.yml +++ b/.github/workflows/ci-comment-failures.yml @@ -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.' }); } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fce2860e53..aac9dce7a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 359b3ee263..d22bcb06bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. diff --git a/examples/README.md.tpl b/docs-template/EXAMPLE_README.md.tpl similarity index 100% rename from examples/README.md.tpl rename to docs-template/EXAMPLE_README.md.tpl diff --git a/docs-template/features.md.tpl b/docs-template/features.md.tpl new file mode 100644 index 0000000000..c63e914a5e --- /dev/null +++ b/docs-template/features.md.tpl @@ -0,0 +1,21 @@ + + + +## 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 %} \ No newline at end of file diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 54432097fb..6f8adfa702 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -1,53 +1,70 @@ -# Cargo Features + + -## 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| diff --git a/src/lib.rs b/src/lib.rs index 22b57943a9..11d3ced9b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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" diff --git a/tools/build-example-pages/Cargo.toml b/tools/build-templated-pages/Cargo.toml similarity index 70% rename from tools/build-example-pages/Cargo.toml rename to tools/build-templated-pages/Cargo.toml index d4af07b0a1..bc570321b9 100644 --- a/tools/build-example-pages/Cargo.toml +++ b/tools/build-templated-pages/Cargo.toml @@ -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" diff --git a/tools/build-example-pages/src/main.rs b/tools/build-templated-pages/src/examples.rs similarity index 89% rename from tools/build-example-pages/src/main.rs rename to tools/build-templated-pages/src/examples.rs index 707f2aa538..b1cdc3ad32 100644 --- a/tools/build-example-pages/src/main.rs +++ b/tools/build-templated-pages/src/examples.rs @@ -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 = examples - .into_iter() - .fold(HashMap::>::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 { .unwrap() .get("metadata") .as_ref() - .unwrap()["category"] + .unwrap()["example_category"] .clone() .as_array_of_tables() .unwrap() @@ -151,3 +101,41 @@ fn parse_categories() -> HashMap { }) .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 = examples + .into_iter() + .fold(HashMap::>::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"); + } +} diff --git a/tools/build-templated-pages/src/features.rs b/tools/build-templated-pages/src/features.rs new file mode 100644 index 0000000000..f9fbd66951 --- /dev/null +++ b/tools/build-templated-pages/src/features.rs @@ -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 { + Some(self.cmp(other)) + } +} + +fn parse_features(panic_on_missing: bool) -> Vec { + let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap(); + let manifest = manifest_file.parse::().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"); + } +} diff --git a/tools/build-templated-pages/src/main.rs b/tools/build-templated-pages/src/main.rs new file mode 100644 index 0000000000..435cc33687 --- /dev/null +++ b/tools/build-templated-pages/src/main.rs @@ -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); + } +}