mod bsn;1mod scene_component;23use proc_macro::TokenStream;4use syn::{parse_macro_input, DeriveInput};56/// Creates a `Scene` using BSN (Bevy Scene Notation) syntax.7///8/// BSN is a concise DSL for defining Bevy scenes as hierarchical collections9/// of entities and components.10/// While BSN's syntax largely follows Rust, it has quite a few features.11/// Don't feel like you need to master all of it before you begin.12/// Start simple and check back on the documentation as you run into problems.13///14/// Trying to decipher a strange combination of glyphs? Jump to the **Syntax Reference** section below.15///16/// ## Basic usage17///18/// Let's begin by spawning a single named entity with a couple of components:19///20/// ```rust, ignore21/// #[derive(Component, Default, Clone)]22/// struct Score(u32);23///24/// #[derive(Component, FromTemplate)]25/// struct Health { current: u32, max: u32 }26///27/// // Spawns a single entity named "Player" with a Score and Health component.28/// world.spawn_scene(bsn! {29/// #Player30/// Score(0)31/// Health { current: 100, max: 100 }32/// });33/// ```34///35/// Each `bsn!` block describes a single root entity.36///37/// ## Children and relationships38///39/// You can add child entities with the `Children` component.40/// To begin a new child entity, separate it from the previous one with a comma.41/// Separate components on the same entity with whitespace.42///43/// ```rust, ignore44/// // ONE child entity with both the Head and Body components:45/// bsn! {46/// #Player47/// Life48/// Children [Head Body]49/// }50///51/// // TWO child entities — one with Head, one with Body:52/// bsn! {53/// #Player54/// Life55/// Children [Head, Body]56/// }57/// ```58///59/// **Warning:** Separating items with whitespace places them on the **same** entity;60/// separating them with commas creates **separate** entities.61/// This is a common source of problems for new BSN users,62/// so be sure to check your commas if an entity seems to be missing!63///64/// Child entities can themselves have components and children, so scenes nest arbitrarily deep:65///66/// ```rust, ignore67/// bsn! {68/// #Town69/// Children [70/// // Parentheses and indentation help clarify the structure of nested scenes,71/// // but are optional — this scene would be the same without them.72/// (73/// #Tavern74/// Children [75/// #Innkeeper,76/// #Barkeep,77/// ]78/// ),79/// (80/// #Blacksmith81/// Children [82/// #Anvil,83/// ]84/// ),85/// ]86/// }87/// ```88///89/// Swap `Children` for any `RelationshipTarget` component to define custom relationships.90///91/// To attach entities to an *existing* entity not created by this scene, use the `ChildOf` component directly.92/// The following example uses [`bsn_list!`] to spawn two entities both attached to a pre-existing root:93///94/// ```rust, ignore95/// let root: Entity = /* some pre-existing entity */;96/// bsn_list! {97/// ( #Child1 ChildOf(root) ),98/// ( #Child2 ChildOf(#Child1) ),99/// }100/// ```101///102/// `ChildOf` accepts either a plain `Entity` value or a named `#Name` reference from the same scope.103///104/// ## Named entity references105///106/// The `#Name` syntax does two things at once: it adds a `Name("Name")` component to the entity107/// and registers it so that other entities in the same scene scope can refer to it by name.108/// To use a named entity as a value — for example, as a component field that holds an `Entity` id —109/// write `#Name` in the value position:110///111/// ```rust, ignore112/// #[derive(Component, FromTemplate)]113/// struct Link(Entity);114///115/// // Spawn a parent named "Hub" with two children that each hold a back-reference to it.116/// bsn! {117/// #Hub118/// Children [119/// Link(#Hub),120/// Link(#Hub),121/// ]122/// }123/// ```124///125/// References can point in any direction within the scene: a parent can reference a descendant,126/// a child can reference a parent, and siblings within the same `Children [...]` block can127/// reference each other.128/// All names in a single `bsn!` call share one scope; names from composed or cached scenes129/// (`my_scene()`, `:my_scene`) live in their own separate scopes and are not visible here.130/// If two scopes both define the same name (e.g. both use `#Player`), each `#Player` resolves131/// to its own entity — there is no conflict or shadowing.132///133/// For dynamic names computed at runtime, use `#{expr}`:134///135/// ```rust, ignore136/// fn reference_named_entity(name: &str) -> impl Scene {137/// bsn! { #{name} }138/// }139/// ```140///141/// In [`bsn_list!`], all root entries share one scope, so sibling root entities can142/// cross-reference each other — see the `bevy_scene` crate docs for more details.143///144/// ## Defaults and patching145///146/// BSN supports *patching*: writing `Health { current: 100 }` creates a patch that sets only147/// `current`. Unmentioned fields keep their values from earlier patches or the type's defaults,148/// and multiple patches to the same component merge rather than overwrite. This works for both149/// `Clone + Default` and `FromTemplate` types.150///151/// The difference between the two is about what values a field can hold at spawn time:152///153/// - **`Clone + Default` types** (e.g. `#[derive(Component, Default, Clone)]`): the simple154/// case. This just works in BSN with no extra derives. All field values must be plain Rust values — the155/// template cannot fill them in correctly based on world or context state.156///157/// - **`FromTemplate` types** (e.g. `#[derive(Component, FromTemplate)]`): needed when a field158/// requires spawn-time context. Use this when a field's type itself implements `FromTemplate`159/// — for example, `Handle<T>` fields that resolve asset path strings, or `Entity` fields that160/// reference named entities in the scene.161///162/// Because each approach generates a different `Template` implementation, `Clone + Default` and163/// `FromTemplate` cannot both be derived on the same type. This would create incoherent trait implementations!164/// Use `Clone + Default` by default, and switch to `FromTemplate` only when you need the extra flexibility it provides.165///166/// ## Expressions and dynamic values167///168/// BSN supports embedding arbitrary Rust expressions anywhere a value is expected,169/// using `{...}` (curly braces):170///171/// ```rust, ignore172/// fn enemy(name: &str, hp: u32) -> impl Scene {173/// let sprite_path = name.to_string() + ".png";174/// bsn! {175/// #{name}176/// Health { current: {hp}, max: {hp} }177/// Sprite { image: {sprite_path} }178/// }179/// }180/// ```181///182/// A `{...}` block can also hold an expression that implements `Scene` or, inside a183/// `Children [...]` block, one that implements `SceneList`:184///185/// ```rust, ignore186/// fn unit_with_armor(unit_base: impl Scene) -> impl Scene {187/// bsn! {188/// {unit_base}189/// Armor(50)190/// }191/// }192/// ```193///194/// `{...}` blocks can also be `unsafe` or `const` (but not both at once without nesting):195///196/// ```rust, ignore197/// fn friendly(people: &[&'static str]) -> impl Scene {198/// bsn! {199/// Friend {200/// name: const {"John"}201/// /// SAFETY: Jesus take the wheel202/// father: unsafe {people.get_unchecked(0)}203/// }204/// }205/// }206/// ```207///208/// **Note:** `.bsn` asset files will not support arbitrary Rust expressions,209/// as we do not intend to require Bevy games to ship a Rust compiler.210///211/// ## Automatic type conversion212///213/// BSN performs some automatic type conversion for you,214/// reducing boilerplate when creating scenes.215///216/// The `bsn!` macro appends `.into()` to expressions (`{...}`), bare identifiers, closures, and string217/// literals when they appear as field values. This means any field assignment that would be218/// valid via Rust's standard [`From`]/[`Into`] traits works transparently in BSN — no explicit219/// cast needed:220///221/// ```rust, ignore222/// #[derive(Component, Default, Clone)]223/// struct Label(String);224///225/// let greeting: &'static str = "Hello";226/// bsn! {227/// // &str → String via Into, no .to_string() required228/// Label(greeting)229/// }230/// ```231///232/// A related, more advanced trick is what makes string literals work as asset paths for `Handle<T>` fields.233/// See the docs on `HandleTemplate` for more information!234///235/// Note: non-string literals are used as-is. Only string literals get `.into()` appended;236/// all other literals (integers, floats, booleans) are emitted directly,237/// so `Health { current: 100 }` assigns `100` without any conversion.238/// If the types don't match (e.g. you forgot to append a decimal point to a float),239/// you'll get a normal Rust type error.240///241/// ## Asset loading242///243/// When a component field is a `Handle<T>`, BSN accepts a string literal in its place.244/// The string is resolved to an asset handle at spawn time via the asset server, reusing245/// an existing handle if the asset is already loaded:246///247/// ```rust, ignore248/// commands.spawn_scene(bsn! {249/// Sprite { image: "player.png" }250/// });251/// ```252///253/// This works for your own `FromTemplate`-derived components too — any `Handle<T>` field254/// automatically accepts asset path strings:255///256/// ```rust, ignore257/// #[derive(Component, FromTemplate)]258/// struct Icon {259/// image: Handle<Image>,260/// tint: Color,261/// }262///263/// commands.spawn_scene(bsn! {264/// Icon { image: "icon.png", tint: Color::WHITE }265/// });266/// ```267///268/// ## Observers269///270/// Use `on` inside `bsn!` to attach an entity observer — a closure that fires when a given271/// `EntityEvent` targets the entity. The first parameter's type determines which event is272/// observed. Multiple observers can be stacked on the same entity, and the closure has full273/// access to the ECS via system parameters:274///275/// ```rust, ignore276/// #[derive(EntityEvent)]277/// struct Damage(u32);278///279/// fn player() -> impl Scene {280/// bsn! {281/// Health { max: 100, current: 100 }282/// on(|ev: On<Damage>, mut query: Query<&mut Health>| {283/// let mut health = query.get_mut(ev.target()).unwrap();284/// health.current = health.current.saturating_sub(ev.0);285/// })286/// }287/// }288///289/// // `move` closures work too, capturing variables from the enclosing scope:290/// fn enemy(bonus_damage: u32) -> impl Scene {291/// bsn! {292/// on(move |ev: On<Damage>, mut query: Query<&mut Health>| {293/// let mut health = query.get_mut(ev.target()).unwrap();294/// health.current = health.current.saturating_sub(ev.0 + bonus_damage);295/// })296/// }297/// }298/// ```299///300/// ## Scene composition and inheritance301///302/// There are two ways to build on an existing scene: **inline composition** and **inheritance**.303///304/// **Inline composition** calls a scene function directly inside `bsn!`. The parent's unresolved305/// templates are merged with the child's and everything resolves together in one pass:306///307/// ```rust, ignore308/// fn base_enemy() -> impl Scene {309/// bsn! {310/// Health { current: 100, max: 100 }311/// Power(10)312/// }313/// }314///315/// // Compose base_enemy() and patch just the fields that differ.316/// fn boss() -> impl Scene {317/// bsn! {318/// base_enemy()319/// Health { max: 500 }320/// Power(50)321/// }322/// }323/// ```324///325/// **Inheritance** uses the `:` prefix. The parent is *pre-resolved* first — its templates are326/// fully flattened into a `ResolvedScene` — and the child's patches are applied on top.327/// When the scene is parameterless, this will "cache" the scene and share it across all inheriting scenes.328/// For larger scenes that are cached and spawned many times, this can be much faster than re-computing329/// the scene each time.330///331/// ```rust, ignore332/// fn boss() -> impl Scene {333/// bsn! {334/// :base_enemy335/// Health { max: 500 }336/// Power(50)337/// }338/// }339///340/// // Asset inheritance (.bsn format not yet released, sorry!):341/// bsn! {342/// :"enemy.bsn"343/// Health { max: 500 }344/// }345/// ```346///347/// | | Function inheritance `:my_scene` | Asset inheritance `:"my_scene.bsn"` | Inline composition `my_scene()` |348/// |---------------------------|-------------------------------------|-------------------------------------|----------------------------------|349/// | Accepts parameters | Yes | No | Yes |350/// | Asset-based | No | Yes | No |351/// | Cached resolution | Parameterless scenes only | Yes | No |352///353/// Prefer scene inheritance over inline composition in general: the expensive scene resolution is cached, saving work during reuse.354/// Inline composition should be reserved for parameterized scenes that vary based on a given input,355/// small scenes that are shared across contexts (like styles),356/// or one-off scenes that do not require reuse.357///358/// /// ## Formatting BSN359///360/// Whitespace, parentheses, and comments have no effect on the generated scene —361/// they exist purely to help you organize and read your code.362///363/// **Whitespace** (spaces, newlines, tabs) separates items on the *same* entity.364/// Use it freely for alignment and to make groupings of both components and entities clearer.365///366/// **Parentheses** group a set of items into one logical unit inside a `[...]` list.367/// Trailing commas are the delimiter used to end one entity and start another,368/// but these can be hard to spot in deeply nested or one-line scenes.369/// Parentheses (often with associated indentation) make the boundary explicit:370///371/// ```rust, ignore372/// bsn! {373/// Children [374/// // Hard to see where one entity ends and the next begins:375/// ComponentA376/// ComponentB,377/// ComponentA378/// ComponentC,379///380/// // Much clearer:381/// (382/// ComponentA383/// ComponentB384/// ),385/// (386/// ComponentA387/// ComponentC388/// ),389/// ]390/// }391/// ```392///393/// ```rust, ignore394/// // Without parentheses, one-line definitions of children are prone to subtle mistakes:395/// bsn! { Children [ComponentA ComponentB, ComponentC ComponentD] }396///397/// // With parentheses, the structure is clear:398/// bsn! { Children [ (ComponentA ComponentB), (ComponentC ComponentD) ] }399/// ```400///401/// **Comments** (`//` line comments and `/* */` block comments) work exactly as in402/// normal Rust and are stripped by the macro before parsing.403///404/// ## Syntax Reference405///406/// ### Components on the same entity vs. separate entities407///408/// | Syntax | Meaning | Explanation |409/// |--------|---------|-------------|410/// | `CompA CompB CompC` | Same entity | **Whitespace** between items — all go on the **same** entity |411/// | `A, B, C` | Separate entities | **Commas** inside `[…]` — each becomes its **own** entity |412/// | `(CompA CompB)` | Entity group | Parentheses group components for readability; equivalent to whitespace alone |413///414/// ### Naming entities415///416/// | Syntax | Meaning | Explanation |417/// |--------|---------|-------------|418/// | `#Name` | Named entity | Adds a `Name("Name")` component and registers the entity for cross-referencing within this scope |419/// | `#{ expr }` | Dynamic name | Names an entity using the result of a Rust expression |420///421/// ### Relationships422///423/// | Syntax | Meaning | Explanation |424/// |--------|---------|-------------|425/// | `Children [s1, s2]` | Add children | Spawns each entry as a child **of this** entity |426/// | `ChildOf(entity)` | Set parent | Makes **this** entity a child of `entity`; accepts a plain `Entity` or a `#Name` reference |427/// | `MyRel [s1, s2]` | Custom relationship | Like `Children`, but uses any `RelationshipTarget` component |428///429/// ### Dynamic values430///431/// | Syntax | Meaning | Explanation |432/// |--------|---------|-------------|433/// | `{ expr }` | Rust expression | Evaluated at spawn time; may be a component, `impl Scene`, or (inside `[…]`) `impl SceneList` |434/// | `field: { expr }` | Expression in field | Embeds a Rust expression as the value of a named field |435///436/// ### Composition and inheritance437///438/// | Syntax | Meaning | Explanation |439/// |--------|---------|-------------|440/// | `my_scene()` | Inline composition | Merges `my_scene`'s unresolved templates into this entity |441/// | `:my_scene` | Inheritance | Pre-resolves `my_scene` first, then patches on top |442/// | `:"path.bsn"` | Asset inheritance | Inherits from a `.bsn` asset file; requires `queue_spawn_scene` |443///444/// ### Observers445///446/// | Syntax | Meaning | Explanation |447/// |--------|---------|-------------|448/// | `on(\|ev: On<Ev>\| { … })` | Observer | Attaches an entity observer that fires when `Ev` targets this entity |449///450/// ### Other Rust syntax451///452/// If you're new to Rust, you might struggle with some of its syntax when you see it in BSN.453/// Here are the most important syntax patterns to be aware of:454///455/// | Syntax | Meaning | Explanation |456/// |--------|---------|-------------|457/// | `MyComponent` | Unit or defaulted struct | A struct with no fields, or in BSN only, with all fields at their defaults |458/// | `MyComponent { field: val, field2: val2 }` | Struct with named fields | Sets named fields; unmentioned fields keep their defaults or values from prior patches |459/// | `MyComponent(val1, val2)` | Tuple struct | Constructs a tuple-struct component |460/// | `MyEnum::Variant` | Enum variant | A value of the `MyEnum` type with the `Variant` variant |461/// | `module::MyComponent` | Path | A module path to a struct type |462/// | `GREEN`` | Constant | A constant value |463/// | `f32::PI` | Associated constant | A constant (here, `PI`) associated with a type (here, `f32`) |464/// | `\|param\| { … }` | Closure | A closure; effectively an unnamed function |465/// | `Vec<T>` | Generic type | A type with a generic type parameter `T` |466/// | `spawn_enemy("orc", 10, true)` | Function call | Calls a function with the given arguments by position |467/// | `spawn_player()` | Argumentless function call | Calls a function that takes no arguments |468/// | `//` | Comment | Line comment; all text after `//` on the same line is ignored |469/// | `/* */` | Block comment | Standard Rust block comment; all text inside `/* */` is ignored |470/// | `bsn! { … }` | Macro call | Calls a macro on the value inside of the braces |471///472/// ## Further reading473///474/// See [`bsn_list!`] if you want to create multiple scenes at once,475/// or want to have multiple root entities.476///477/// See the `bevy_scene` crate docs for a high-level overview of the key concepts.478#[proc_macro]479pub fn bsn(input: TokenStream) -> TokenStream {480crate::bsn::bsn(input)481}482483/// Creates a `SceneList` using BSN (Bevy Scene Notation) syntax.484///485/// This is useful when you want multiple root entities in your scene486/// that do not share a common parent, or if you want to create multiple scenes at once.487///488/// Like in [`bsn!`], commas separate entities,489/// while whitespace separates components on the same entity.490///491/// All root entries in a [`bsn_list!`] share a single name scope, so sibling root entities492/// can cross-reference each other by `#Name`.493/// This is not possible with separate [`bsn!`] calls, and is a key motivation for using [`bsn_list!`].494///495/// See [`bsn!`] for more details on syntax.496/// See the `bevy_scene` crate docs for a high-level overview of the key concepts.497#[proc_macro]498pub fn bsn_list(input: TokenStream) -> TokenStream {499crate::bsn::bsn_list(input)500}501502#[proc_macro_derive(503SceneComponent,504attributes(component, require, relationship, relationship_target, entities, scene)505)]506pub fn derive_scene_component(input: TokenStream) -> TokenStream {507let mut ast = parse_macro_input!(input as DeriveInput);508TokenStream::from(scene_component::derive_scene_component(&mut ast))509}510511512