Skip to content

Commit 4304c0b

Browse files
committed
Added block matching macro
1 parent 9d9bb3a commit 4304c0b

File tree

4 files changed

+84
-2
lines changed

4 files changed

+84
-2
lines changed

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ ahash = "0.8.12"
146146
serde = { version = "1.0.228", features = ["derive"] }
147147
serde_json = "1.0.145"
148148
serde_derive = "1.0.228"
149-
serde_yaml_ng = "0.10.0"
150149
base64 = "0.22.1"
151150

152151
# When if bitcode's latest version has been changed, see if clippy still complains about
@@ -171,7 +170,6 @@ lazy_static = "1.5.0"
171170
quote = "1.0.41"
172171
syn = "2.0.106"
173172
proc-macro2 = "1.0.101"
174-
proc-macro-crate = "3.4.0"
175173
paste = "1.0.15"
176174
maplit = "1.0.2"
177175
macro_rules_attribute = "0.2.2"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use crate::block::JSON_FILE;
2+
use proc_macro::TokenStream;
3+
use quote::quote;
4+
use simd_json::base::{ValueAsObject, ValueAsScalar};
5+
use simd_json::derived::ValueObjectAccess;
6+
7+
struct Input {
8+
name: String,
9+
id_var: syn::Expr,
10+
}
11+
12+
impl syn::parse::Parse for Input {
13+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
14+
let name_lit: syn::LitStr = input.parse()?;
15+
let _comma: syn::Token![,] = input.parse()?;
16+
let id_var: syn::Expr = input.parse()?;
17+
Ok(Input {
18+
name: name_lit.value(),
19+
id_var,
20+
})
21+
}
22+
}
23+
24+
// match_block!("stone", block_id); -> "if block_id == BlockId(1) { ... }
25+
// match_block!("dirt", block_id); -> "if block_name == BlockId( { ... }
26+
pub fn matches_block(input: TokenStream) -> TokenStream {
27+
let input = syn::parse_macro_input!(input as Input);
28+
let block_name = &input.name;
29+
let block_name = if block_name.starts_with("minecraft:") {
30+
block_name.to_string()
31+
} else {
32+
format!("minecraft:{}", block_name)
33+
};
34+
let block_id_var = &input.id_var;
35+
let mut buf = JSON_FILE.to_vec();
36+
let v = simd_json::to_owned_value(&mut buf).unwrap();
37+
let filtered_names = v
38+
.as_object()
39+
.unwrap()
40+
.iter()
41+
.filter(|(_, v)| v.get("name").as_str() == Some(&block_name))
42+
.map(|(k, v)| (k.parse::<u32>().unwrap(), v))
43+
.collect::<Vec<_>>();
44+
if filtered_names.is_empty() {
45+
return syn::Error::new_spanned(
46+
&input.id_var,
47+
format!("Block name '{}' not found in registry", block_name),
48+
)
49+
.to_compile_error()
50+
.into();
51+
}
52+
let mut arms = Vec::new();
53+
for (id, _) in filtered_names {
54+
arms.push(quote! {
55+
#block_id_var == BlockId(#id)
56+
});
57+
}
58+
let joined = quote! {
59+
#(#arms)||*
60+
};
61+
joined.into()
62+
}

src/lib/derive_macros/src/block/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use syn::parse::{Parse, ParseStream};
66
use syn::punctuated::Punctuated;
77
use syn::{braced, Expr, Ident, Lit, LitStr, Result, Token};
88

9+
pub(crate) mod matches;
10+
911
const JSON_FILE: &[u8] = include_bytes!("../../../../../assets/data/blockstates.json");
1012

1113
struct Input {

src/lib/derive_macros/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(proc_macro_quote)]
22

3+
use block::matches;
34
use proc_macro::TokenStream;
45

56
mod block;
@@ -122,3 +123,22 @@ pub fn build_registry_packets(input: TokenStream) -> TokenStream {
122123
pub fn block(input: TokenStream) -> TokenStream {
123124
block::block(input)
124125
}
126+
127+
/// A macro to check if a block ID matches a given block name at compile time.
128+
/// Usage:
129+
/// ```
130+
/// # use ferrumc_macros::{match_block};
131+
/// # use ferrumc_world::block_id::BlockId;
132+
/// let block_id = BlockId(1);
133+
/// if match_block!("stone", block_id) {
134+
/// // do something
135+
/// }
136+
/// ```
137+
/// Unfortunately, due to current limitations in Rust's proc macros, you will need to import the
138+
/// `BlockId` struct manually.
139+
///
140+
/// The `minecraft:` namespace is optional and will be added automatically if not present.
141+
#[proc_macro]
142+
pub fn match_block(input: TokenStream) -> TokenStream {
143+
matches::matches_block(input)
144+
}

0 commit comments

Comments
 (0)