Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added #[proto_comp(with=...)] and #[proto_comp(into=...)] attributes #8

Merged
merged 1 commit into from
Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ path = "examples/basic.rs"
name = "bundles"
path = "examples/bundles.rs"

[[example]]
name = "attributes"
path = "examples/attributes.rs"

[[example]]
name = "templates"
path = "examples/templates.rs"
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,19 @@ For simple components, `ProtoComponent` may be derived:
```rust
use bevy_proto::ProtoComponent;

#[derive(Serialize, Deserialize, ProtoComponent)]
#[derive(Clone, Serialize, Deserialize, ProtoComponent, Component)]
struct Movement {
#[proto_comp(Copy)]
speed: u16
}

// Also works on tuple structs:
#[derive(Serialize, Deserialize, ProtoComponent)]
struct Inventory (
// Optional: #[proto_comp(Clone)]
#[derive(Clone, Serialize, Deserialize, ProtoComponent, Component)]
struct Inventory (
Option<Vec<String>>
)
);
```

> By default, the fields of a `ProtoComponent` are cloned into spawned entities. This can be somewhat controlled via the `proto_comp` attribute, which can tell the compiler to use the `Copy` trait instead.
> By default, the `ProtoComponent` is cloned into spawned entities.

Otherwise, you can define them manually (the two attributes are required with this method):

Expand All @@ -170,6 +168,8 @@ impl ProtoComponent for Inventory {
```

> A `ProtoComponent` does *not* need to be a component itself. It can be used purely as a [DTO](https://en.wikipedia.org/wiki/Data_transfer_object) to generate other components or bundles. You have full access to the `EntityCommands` so you can insert bundles or even multiple components at once.
>
> Other ways of generating components from non-component `ProtoComponent` structs can be found in the [attributes](https://github.com/MrGVSV/bevy_proto/blob/main/examples/attributes.rs) example.

### Defining the Prototype

Expand Down
5 changes: 5 additions & 0 deletions assets/prototypes/emoji_angry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
name: "Angry"
components:
- type: Face
value: Frowning
6 changes: 6 additions & 0 deletions assets/prototypes/emoji_happy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
name: "Happy"
components:
- type: EmojiDef
value:
emoji: 😁
6 changes: 6 additions & 0 deletions assets/prototypes/emoji_sad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
name: "Sad"
components:
- type: EmojiDef
value:
emoji: 😢
5 changes: 5 additions & 0 deletions assets/prototypes/emoji_silly.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
name: "Silly"
components:
- type: Mood
value: Silly
71 changes: 71 additions & 0 deletions bevy_proto_derive/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::{Error, LitStr, Path, Result, Token};

use crate::constants::{INTO_IDENT, WITH_IDENT};

/// ProtoComponent attributes applied on structs
pub(crate) enum ProtoCompAttr {
/// Captures the `#[proto_comp(into = "ActualComponent")]` attribute
///
/// This is used to specify a separate Component that this marked struct will be cloned into.
///
/// Generates the following code:
/// ```rust
/// let component: ActualComponent = self.clone().into();
/// commands.insert(component);
/// ```
Into(Ident),
/// Captures the `#[proto_comp(with = "my_function")]` attribute
///
/// This is used to specify a custom function with which custom Components will be creatde and/or inserted.
/// This is essentially identical to just simply implementing `ProtoComponent` yourself.
///
/// Generates the following code:
/// ```rust
/// my_function(self, commands, asset_server);
/// ```
With(Ident),
}

impl Parse for ProtoCompAttr {
fn parse(input: ParseStream) -> Result<Self> {
let path: Path = input.parse()?;
let _: Token![=] = input.parse()?;
let item: LitStr = input.parse()?;
let ident = format_ident!("{}", item.value());

if path == WITH_IDENT {
Ok(Self::With(ident))
} else if path == INTO_IDENT {
Ok(Self::Into(ident))
} else {
Err(Error::new(
Span::call_site(),
format!("Unexpected path '{:?}'", path),
))
}
}
}

impl ToTokens for ProtoCompAttr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Into(ident) => {
let into_ident = quote! {
let cloned = self.clone();
let component: #ident = cloned.into();
commands.insert(component);
};
into_ident.to_tokens(tokens);
}
Self::With(ident) => {
let with_ident = quote! {
#ident(self, commands, asset_server);
};
with_ident.to_tokens(tokens);
}
}
}
}
5 changes: 2 additions & 3 deletions bevy_proto_derive/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ impl PartialEq<Symbol> for syn::Path {
}
}

pub(crate) const ATTR_IDENT: Symbol = Symbol("proto_comp");
pub(crate) const COPY_IDENT: Symbol = Symbol("Copy");
pub(crate) const CLONE_IDENT: Symbol = Symbol("Clone");
pub(crate) const WITH_IDENT: Symbol = Symbol("with");
pub(crate) const INTO_IDENT: Symbol = Symbol("into");
104 changes: 0 additions & 104 deletions bevy_proto_derive/src/fields.rs

This file was deleted.

Loading