diff options
| author | mat <git@matdoes.dev> | 2025-12-15 13:53:46 +0930 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2025-12-15 13:53:46 +0930 |
| commit | aab9f56da20a36347fe31557377e07a47c114dc6 (patch) | |
| tree | 15c81634d92eed995a85a65b05584ae216207163 | |
| parent | 9745766cff8907e6c6bb87b4ab852acaeadd6ef9 (diff) | |
| download | azalea-drasl-aab9f56da20a36347fe31557377e07a47c114dc6.tar.xz | |
improve docs by enabling scraped examples and bevy trait tags
| -rw-r--r-- | Cargo.toml | 13 | ||||
| -rw-r--r-- | azalea/Cargo.toml | 6 | ||||
| -rw-r--r-- | azalea/README.md | 62 | ||||
| -rw-r--r-- | azalea/src/builder.rs | 13 | ||||
| -rw-r--r-- | azalea/src/swarm/builder.rs | 4 | ||||
| -rw-r--r-- | docs-rs/README.md | 17 | ||||
| -rw-r--r-- | docs-rs/trait-tags.html | 177 |
7 files changed, 253 insertions, 39 deletions
@@ -115,6 +115,19 @@ azalea-world = { path = "azalea-world", version = "0.14.0" } # https://users.rust-lang.org/t/to-string-vs-to-owned-for-string-literals/1441 str_to_string = "warn" +[workspace.lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] } + +[workspace.metadata.docs.rs] +rustc-args = ["--cfg", "docsrs_dep"] +rustdoc-args = [ + "--cfg", + "docsrs_dep", + "--html-after-content", + "docs-rs/trait-tags.html", +] +cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] + # --- Profile Settings --- [profile.release] diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml index 15627bc1..e4adb426 100644 --- a/azalea/Cargo.toml +++ b/azalea/Cargo.toml @@ -59,6 +59,12 @@ serde = ["dep:serde", "azalea-registry/serde", "azalea-world/serde"] packet-event = ["azalea-client/packet-event"] online-mode = ["azalea-client/online-mode"] +[[example]] +name = "testbot" +# setting this to true for one example makes it work for all of them. +# see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#scrape-examples +doc-scrape-examples = true + [[bench]] name = "pathfinder" harness = false diff --git a/azalea/README.md b/azalea/README.md index e44036c3..ca081f8e 100644 --- a/azalea/README.md +++ b/azalea/README.md @@ -1,35 +1,6 @@ Azalea is a framework for creating Minecraft bots. -Also see the project [README](https://github.com/azalea-rs/azalea) for a higher-level overview of Azalea. - -# Installation - -First, install Rust nightly with `rustup install nightly` and `rustup default nightly`. - -Then, use one of the following commands to add Azalea to your project: - -- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea` -- Latest "stable" release: `cargo add azalea` - -## Optimization - -For faster compile times, create a `.cargo/config.toml` file in your project and copy -[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds.toml) -into it. You may have to install the LLD linker. - -For faster performance in debug mode, add the following code to your -Cargo.toml: - -```toml -[profile.dev] -opt-level = 1 -[profile.dev.package."*"] -opt-level = 3 -``` - -# Documentation - -The documentation for the latest Azalea crates.io release is available at [docs.rs/azalea](https://docs.rs/azalea/latest/azalea/) and the docs for the latest bleeding-edge (git) version are at [azalea.matdoes.dev](https://azalea.matdoes.dev/azalea/). +See the project [README](https://github.com/azalea-rs/azalea) for a higher-level overview of Azalea. # Examples @@ -81,6 +52,35 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> { There are more examples in the [examples directory](https://github.com/azalea-rs/azalea/tree/main/azalea/examples). You may also find it helpful to read the code for [other people's Azalea bots](https://github.com/azalea-rs/azalea#real-world-bots-using-azalea). +# Installation + +First, install Rust nightly with `rustup install nightly` and `rustup default nightly`. + +Then, use one of the following commands to add Azalea to your project: + +- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea` +- Latest "stable" release: `cargo add azalea` + +## Optimization + +For faster compile times, create a `.cargo/config.toml` file in your project and copy +[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds.toml) +into it. You may have to install the LLD linker. + +For faster performance in debug mode, add the following code to your +Cargo.toml: + +```toml +[profile.dev] +opt-level = 1 +[profile.dev.package."*"] +opt-level = 3 +``` + +# Documentation + +The documentation for the latest Azalea crates.io release is available at [docs.rs/azalea](https://docs.rs/azalea/latest/azalea/) and the docs for the latest bleeding-edge (git) version are at [azalea.matdoes.dev](https://azalea.matdoes.dev/azalea/). + # Swarms Azalea lets you create "swarms", which are a group of bots in the same world that can perform actions together. See [testbot](https://github.com/azalea-rs/azalea/blob/main/azalea/examples/testbot/main.rs) for an example. Also, if you're using swarms, you should also `use` both `azalea::prelude::*` and `azalea::swarm::prelude::*`. @@ -89,7 +89,7 @@ Azalea lets you create "swarms", which are a group of bots in the same world tha Azalea uses [Bevy ECS](https://docs.rs/bevy_ecs) internally to store information about the world and clients. Bevy plugins are more powerful than async handler functions, but more difficult to use. See [pathfinder](https://github.com/azalea-rs/azalea/blob/main/azalea/src/pathfinder/mod.rs) as an example of how to make a plugin. You can then enable a plugin by adding `.add_plugin(ExamplePlugin)` in your client/swarm builder. -Everything in Azalea is implemented as a Bevy plugin, which means you can disable default behaviors (like, physics or chat signing) by disabling built-in plugins. See [`SwarmBuilder::new_without_plugins`] to learn how to do that. +Everything in Azalea is implemented as a Bevy plugin, which means you can disable default behaviors (like, physics or chat signing) by disabling built-in plugins. See [`SwarmBuilder::new_without_plugins`](swarm::SwarmBuilder::new_without_plugins) to learn how to do that. Also note that just because something is an entity in the ECS doesn't mean that it's a Minecraft entity. You can filter for that by having `With<MinecraftEntityId>` as a filter. diff --git a/azalea/src/builder.rs b/azalea/src/builder.rs index d0318424..91fb9d5e 100644 --- a/azalea/src/builder.rs +++ b/azalea/src/builder.rs @@ -11,8 +11,8 @@ use crate::{ swarm::{self, SwarmBuilder}, }; -/// A builder for creating new [`Client`]s. This is the recommended way of -/// making a bot. +/// A builder for creating new [`Client`](crate::Client)s. This is the +/// recommended way of making a bot. /// /// ```no_run /// # use azalea::prelude::*; @@ -84,8 +84,9 @@ impl ClientBuilder<NoState, ()> { } } - /// Set the function that's called every time a bot receives an [`Event`]. - /// This is the way to handle normal per-bot events. + /// Set the function that's called every time a bot receives an + /// [`Event`](crate::Client). This is the way to handle normal per-bot + /// events. /// /// Currently, you can have up to one client handler. /// @@ -156,8 +157,8 @@ where self } - /// Build this `ClientBuilder` into an actual [`Client`] and join the given - /// server. + /// Build this `ClientBuilder` into an actual [`Client`](crate::Client) and + /// join the given server. /// /// If the client can't join, it'll keep retrying forever until it can. /// diff --git a/azalea/src/swarm/builder.rs b/azalea/src/swarm/builder.rs index 412ec67e..4eae7eab 100644 --- a/azalea/src/swarm/builder.rs +++ b/azalea/src/swarm/builder.rs @@ -137,8 +137,8 @@ where SS: Default + Send + Sync + Clone + Resource + 'static, { /// Set the function that's called every time a bot receives an - /// [`enum@Event`]. This is the intended way to handle normal per-bot - /// events. + /// [`Event`](crate::Event). This is the intended way to handle + /// normal per-bot events. /// /// Currently you can have up to one handler. /// diff --git a/docs-rs/README.md b/docs-rs/README.md new file mode 100644 index 00000000..3be1087a --- /dev/null +++ b/docs-rs/README.md @@ -0,0 +1,17 @@ +## Docs.rs Extensions + +This directory includes some templates and styling to extend and modify [rustdoc]'s output +for Azalea's documentation on [docs.rs]. + +See [Bevy's documentation](https://github.com/bevyengine/bevy/tree/main/docs-rs) for more info. + +## Local Testing + +Build the documentation with the extension enabled like this: + +```bash +RUSTDOCFLAGS="--html-after-content docs-rs/trait-tags.html --cfg docsrs_dep" RUSTFLAGS="--cfg docsrs_dep" cargo doc --no-deps --package <package_name> +``` + +[rustdoc]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html +[docs.rs]: https://docs.rs diff --git a/docs-rs/trait-tags.html b/docs-rs/trait-tags.html new file mode 100644 index 00000000..e34614ee --- /dev/null +++ b/docs-rs/trait-tags.html @@ -0,0 +1,177 @@ +<script> + // Adds tags to documentation pages for common Bevy traits like `Component` or `Resource`. + // This makes it easier to see at a glance what types are used for. + // + // This extension is passed to `rustdoc` using the `--html-after-content` flag. + + // Traits that we want to show as tags. + // Order determines sort order of items in listings. + const bevyTraits = [ + 'Plugin', + 'PluginGroup', + 'Component', + 'Resource', + 'Asset', + 'Event', + 'Message', + 'ScheduleLabel', + 'SystemSet', + 'SystemParam', + 'Relationship', + 'RelationshipTarget' + ]; + + // Find all traits that are implemented by the current type. + const implementedBevyTraits = findImplementedBevyTraits(document); + + // If we found any implemented traits, add them as tags to the top of the page. + if (implementedBevyTraits.size > 0) { + // Create a container for the tags. + const heading = document.body.querySelector(".main-heading h1"); + const tagContainer = document.createElement('div'); + tagContainer.className = 'bevy-tag-container'; + heading.appendChild(tagContainer); + + // Check if an implemented trait has a `type Mutability = Immutable` associated type. + // This is used to determine if a `Component` is immutable or not. + // TODO: Ideally we should just check the associated types of the `Component` trait, + // but the docs.rs layout makes it tricky to do so in a robust way. + const associatedTypeHeader = document.querySelectorAll(".trait-impl.associatedtype .code-header"); + const isImmutable = [...associatedTypeHeader].some(el => el.innerText.includes('type Mutability = Immutable')); + + // Create a tag for each implemented trait. + for (let [tagName, href] of implementedBevyTraits) { + if (tagName == 'Component' & isImmutable) { + tagName = 'Immutable Component'; + } + + // Create the tag and append it to the container. + tagContainer.appendChild(createBevyTag(tagName, href)); + } + } + + function findImplementedBevyTraits(doc) { + // Traits that are implemented by the current type. + // The key is the trait name, and the value is the href to the trait's documentation. + const implementedTraits = new Map(); + + // Find all trait implementation headers. + const allTraitHeaders = doc.body.querySelectorAll( + '#trait-implementations-list .impl .code-header, #blanket-implementations-list .impl .code-header' + ); + + for (const header of allTraitHeaders) { + // We can extract the trait name by removing any generics and splitting the string by spaces. + // This results in ['impl', 'TraitName', 'for', 'TypeName']. + const traitName = removeGenerics(header.innerText).split(' ')[1].trim(); + + // Find the link to the trait if the anchor element exists. + // Otherwise, the trait is just in plain text. + const traitLinkEl = [...header.children].find(el => el.getAttribute('href')?.includes(`trait.${traitName}.html`)); + const href = traitLinkEl?.getAttribute('href'); + + implementedTraits.set(traitName, href); + } + + const implementedBevyTraits = new Map( + [...implementedTraits].filter(([traitName, _]) => bevyTraits.find((x) => x == traitName)) + ); + + return implementedBevyTraits; + } + + // Helper function to remove generics from a string of Rust code. + // For example, 'Vec<T>' would become 'Vec'. + function removeGenerics(str) { + // Remove the innermost generics. + const newStr = str.replace(/<([^<>])*>/g, ''); + + // If there are still generics, perform the removal again recursively. + if (newStr !== str) { + return removeGenerics(newStr); + } + + // No more generics to remove. + return newStr; + } + + // Helper function to create a tag element with the given name and href, + // if available. + function createBevyTag(tagName, href) { + const el = document.createElement('a'); + const kebabCaseName = tagName.toLowerCase().replace(' ', '-'); + + if (href) { + el.setAttribute('href', href); + } + + el.innerText = tagName; + el.className = `bevy-tag ${kebabCaseName}-tag`; + return el; + } +</script> + +<style> + .bevy-tag-container { + padding: 0.5rem 0; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + } + + .bevy-tag { + display: flex; + align-items: center; + width: fit-content; + height: 1.5rem; + padding: 0 0.5rem; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: normal; + color: white; + } + + .bevy-tag { + background-color: var(--tag-color); + } + + .component-tag, + .immutable-component-tag { + --tag-color: oklch(50% 27% 80); + } + + .resource-tag { + --tag-color: oklch(50% 27% 110); + } + + .asset-tag { + --tag-color: oklch(50% 27% 0); + } + + .event-tag { + --tag-color: oklch(50% 27% 310); + } + + .message-tag { + --tag-color: oklch(50% 27% 190); + } + + .plugin-tag, + .plugingroup-tag { + --tag-color: oklch(50% 27% 50); + } + + .schedulelabel-tag, + .systemset-tag { + --tag-color: oklch(50% 27% 270); + } + + .systemparam-tag { + --tag-color: oklch(50% 27% 240); + } + + .relationship-tag, + .relationshiptarget-tag { + --tag-color: oklch(50% 27% 150); + } +</style>
\ No newline at end of file |
