11use proc_macro:: TokenStream ;
22use quote:: quote;
33use simd_json:: prelude:: { ValueAsObject , ValueAsScalar , ValueObjectAccess } ;
4+ use simd_json:: { OwnedValue , StaticNode } ;
45use syn:: parse:: { Parse , ParseStream } ;
56use syn:: punctuated:: Punctuated ;
6- use syn:: { braced, Expr , Ident , LitStr , Result , Token } ;
7+ use syn:: { braced, Expr , Ident , Lit , LitStr , Result , Token } ;
78
89const JSON_FILE : & [ u8 ] = include_bytes ! ( "../../../../../assets/data/blockstates.json" ) ;
910
@@ -73,179 +74,147 @@ pub fn block(input: TokenStream) -> TokenStream {
7374 . as_object ( )
7475 . unwrap ( )
7576 . iter ( )
76- . filter ( |v| {
77- let name_in_json = v. 1 . get ( "name" ) ;
78- if let Some ( resolved_name) = name_in_json {
79- resolved_name. as_str ( ) == Some ( & name_str)
80- } else {
81- false
82- }
83- } )
84- . map ( |v| ( v. 0 . parse :: < u32 > ( ) . unwrap ( ) , v. 1 ) )
77+ . filter ( |( _, v) | v. get ( "name" ) . as_str ( ) == Some ( & name_str) )
78+ . map ( |( k, v) | ( k. parse :: < u32 > ( ) . unwrap ( ) , v) )
8579 . collect :: < Vec < _ > > ( ) ;
86- if let Some ( opts) = opts {
87- let mut props = vec ! [ ] ;
88- for kv in opts. pairs . iter ( ) {
89- let key = kv. key . to_string ( ) ;
90- let value = match & kv. value {
91- Expr :: Lit ( expr_lit) => {
92- if let syn:: Lit :: Str ( lit_str) = & expr_lit. lit {
93- lit_str. value ( )
94- } else if let syn:: Lit :: Bool ( lit_bool) = & expr_lit. lit {
95- lit_bool. value . to_string ( )
96- } else if let syn:: Lit :: Int ( lit_int) = & expr_lit. lit {
97- lit_int. base10_digits ( ) . to_string ( )
98- } else {
99- return syn:: Error :: new_spanned (
80+
81+ let Some ( opts) = opts else {
82+ if filtered_names. is_empty ( ) {
83+ return syn:: Error :: new_spanned (
84+ name. clone ( ) ,
85+ format ! ( "block '{}' not found in blockstates.json" , name_str) ,
86+ )
87+ . to_compile_error ( )
88+ . into ( ) ;
89+ }
90+ if filtered_names. len ( ) > 1 {
91+ let properties = get_properties ( & filtered_names) ;
92+ return syn:: Error :: new_spanned (
93+ name_str. clone ( ) ,
94+ format ! (
95+ "block '{}' has multiple variants, please specify properties. Available properties: {}" ,
96+ name_str, pretty_print_props( & properties)
97+ ) ,
98+ )
99+ . to_compile_error ( )
100+ . into ( ) ;
101+ }
102+ let first = filtered_names. iter ( ) . next ( ) . unwrap ( ) . 0 ;
103+ return quote ! { BlockId ( #first) } . into ( ) ;
104+ } ;
105+
106+ let props = opts
107+ . pairs
108+ . iter ( )
109+ . map ( |kv| {
110+ Ok ( (
111+ kv. key . to_string ( ) ,
112+ match & kv. value {
113+ Expr :: Lit ( v) => match & v. lit {
114+ Lit :: Str ( v) => v. value ( ) ,
115+ Lit :: Bool ( v) => v. value . to_string ( ) ,
116+ Lit :: Int ( v) => v. base10_digits ( ) . to_string ( ) ,
117+ _ => return Err ( syn:: Error :: new_spanned (
100118 & kv. value ,
101119 "only string, bool, and int literals are supported as property values" ,
102- )
103- . to_compile_error ( )
104- . into ( ) ;
105- }
106- }
107- _ => {
108- return syn:: Error :: new_spanned (
109- & kv. value ,
110- "only string, bool, and int literals are supported as property values" ,
111- )
112- . to_compile_error ( )
113- . into ( ) ;
114- }
115- } ;
116- props. push ( ( key, value) ) ;
117- }
118- let mut matched = vec ! [ ] ;
119- ' outer: for ( id, v) in filtered_names. clone ( ) {
120- if let Some ( obj) = v. as_object ( ) {
121- if let Some ( block_props) = obj. get ( "properties" ) . and_then ( |p| p. as_object ( ) ) {
122- for ( k, v) in block_props. iter ( ) {
123- // Convert to a string
124- let converted = if let Some ( s) = v. as_str ( ) {
125- s. to_string ( )
126- } else if let Some ( b) = v. as_bool ( ) {
127- b. to_string ( )
128- } else if let Some ( n) = v. as_i64 ( ) {
129- n. to_string ( )
130- } else {
131- // unsupported property type
132- continue ' outer;
133- } ;
134- // property is a single string
135- if let Some ( ( _, val) ) = props. iter ( ) . find ( |( key, _) | key == k) {
136- if converted != * val {
137- continue ' outer; // no match
138- }
139- } else {
140- continue ' outer; // property not specified
141- }
120+ ) ) ,
121+ } ,
122+ _ => {
123+ return Err ( syn:: Error :: new_spanned (
124+ & kv. value ,
125+ "only string, bool, and int literals are supported as property values" ,
126+ ) )
142127 }
143- matched. push ( id) ;
144- } else if !props. is_empty ( ) {
145- continue ' outer; // properties specified but block has none
146- } else {
147- matched. push ( id) ; // no properties to match, accept this block
128+ } ,
129+ ) )
130+ } )
131+ . collect :: < Result < std:: collections:: HashMap < String , String > > > ( ) ;
132+
133+ let props = match props {
134+ Ok ( props) => props,
135+ Err ( err) => return err. to_compile_error ( ) . into ( ) ,
136+ } ;
137+
138+ let matched = filtered_names
139+ . iter ( )
140+ . filter ( |( _, v) | {
141+ if let Some ( map) = v. get ( "properties" ) . as_object ( ) {
142+ // eq impl from halfbwown
143+ if map. len ( ) != props. len ( ) {
144+ return false ;
148145 }
146+ return map. iter ( ) . all ( |( key, value) | {
147+ let converted = match value {
148+ OwnedValue :: Static ( StaticNode :: Bool ( v) ) => v. to_string ( ) ,
149+ OwnedValue :: Static ( StaticNode :: I64 ( v) ) => v. to_string ( ) ,
150+ OwnedValue :: String ( v) => v. to_string ( ) ,
151+ _ => return false ,
152+ } ;
153+ props. get ( key) . is_some_and ( |v| * converted == * v)
154+ } ) ;
149155 }
150- }
151- if matched. len ( ) > 1 {
152- syn:: Error :: new_spanned (
156+ false
157+ } )
158+ . map ( |( id, _) | * id)
159+ . collect :: < Vec < u32 > > ( ) ;
160+
161+ if matched. is_empty ( ) {
162+ let properties = get_properties ( & filtered_names) ;
163+ if properties. is_empty ( ) {
164+ return syn:: Error :: new_spanned (
153165 name_str. clone ( ) ,
154- format ! ( "block '{}' with specified properties has multiple variants, please refine properties" , name_str) ,
155- )
156- . to_compile_error ( )
157- . into ( )
158- } else if matched. is_empty ( ) {
159- if get_properties_for_id ( name_str. clone ( ) ) . is_empty ( ) {
160- syn:: Error :: new_spanned (
166+ format ! (
167+ "block '{}' has no properties but the following properties were given: {}" ,
161168 name_str. clone( ) ,
162- format ! (
163- "block '{}' has no properties but the following properties were given: {}" ,
164- name_str. clone( ) ,
165- pretty_print_given_props( opts)
166- ) ,
167- )
168- . to_compile_error ( )
169- . into ( )
170- } else {
171- syn:: Error :: new_spanned (
169+ pretty_print_given_props( opts)
170+ ) ,
171+ )
172+ . to_compile_error ( )
173+ . into ( ) ;
174+ } else {
175+ return syn:: Error :: new_spanned (
172176 name_str. clone ( ) ,
173177 format ! (
174178 "no variant of block '{}' matches the specified properties. Available properties: {}" ,
175- name_str. clone( ) , pretty_print_props( & get_properties_for_id ( name_str ) )
179+ name_str. clone( ) , pretty_print_props( & properties )
176180 ) ,
177181 )
178182 . to_compile_error ( )
179- . into ( )
180- }
181- } else {
182- let res = matched[ 0 ] ;
183- quote ! { BlockId ( #res) } . into ( )
183+ . into ( ) ;
184184 }
185- } else if filtered_names. len ( ) > 1 {
186- syn:: Error :: new_spanned (
187- name_str. clone ( ) ,
188- format ! (
189- "block '{}' has multiple variants, please specify properties. Available properties: {}" ,
190- name_str. clone( ) , pretty_print_props( & get_properties_for_id( name_str) )
191- ) ,
192- )
193- . to_compile_error ( )
194- . into ( )
195- } else if filtered_names. is_empty ( ) {
196- syn:: Error :: new_spanned (
197- name. clone ( ) ,
198- format ! ( "block '{}' not found in blockstates.json" , name_str) ,
199- )
200- . to_compile_error ( )
201- . into ( )
202- } else {
203- let first = filtered_names[ 0 ] . 0 ;
204- quote ! { BlockId ( #first) } . into ( )
205185 }
186+ if matched. len ( ) > 1 {
187+ return syn:: Error :: new_spanned (
188+ name_str. clone ( ) ,
189+ format ! ( "block '{}' with specified properties has multiple variants, please refine properties" , name_str) ,
190+ )
191+ . to_compile_error ( )
192+ . into ( ) ;
193+ }
194+
195+ let res = matched[ 0 ] ;
196+ quote ! { BlockId ( #res) } . into ( )
206197}
207198
208- fn get_properties_for_id ( id : String ) -> Vec < ( String , String ) > {
209- // Iterate all blocks and find all with this as the "name" field
210- let mut buf = JSON_FILE . to_vec ( ) ;
211- let v = simd_json:: to_owned_value ( & mut buf) . unwrap ( ) ;
212- let filtered_names = v
213- . as_object ( )
214- . unwrap ( )
199+ fn get_properties ( filtered_names : & [ ( u32 , & OwnedValue ) ] ) -> Vec < ( String , String ) > {
200+ filtered_names
215201 . iter ( )
216- . filter ( |v| {
217- let name_in_json = v. 1 . get ( "name" ) ;
218- if let Some ( resolved_name) = name_in_json {
219- resolved_name. as_str ( ) == Some ( & id)
220- } else {
221- false
222- }
202+ . filter_map ( |( _, v) | v. get ( "properties" ) . and_then ( |v| v. as_object ( ) ) )
203+ . flat_map ( |v| {
204+ v. iter ( ) . filter_map ( |( k, v) | {
205+ let converted = match v {
206+ OwnedValue :: Static ( StaticNode :: Bool ( v) ) => v. to_string ( ) ,
207+ OwnedValue :: Static ( StaticNode :: I64 ( v) ) => v. to_string ( ) ,
208+ OwnedValue :: String ( v) => v. to_string ( ) ,
209+ _ => return None ,
210+ } ;
211+ Some ( ( k. clone ( ) , converted) )
212+ } )
223213 } )
224- . map ( |v| ( v. 0 . parse :: < u32 > ( ) . unwrap ( ) , v. 1 ) )
225- . collect :: < Vec < _ > > ( ) ;
226- let mut all_props = vec ! [ ] ;
227- for ( _, v) in filtered_names {
228- if let Some ( obj) = v. as_object ( ) {
229- if let Some ( block_props) = obj. get ( "properties" ) . and_then ( |p| p. as_object ( ) ) {
230- for ( k, v) in block_props. iter ( ) {
231- let converted = if let Some ( s) = v. as_str ( ) {
232- s. to_string ( )
233- } else if let Some ( b) = v. as_bool ( ) {
234- b. to_string ( )
235- } else if let Some ( n) = v. as_i64 ( ) {
236- n. to_string ( )
237- } else {
238- continue ; // unsupported property type
239- } ;
240- all_props. push ( ( k. clone ( ) , converted) ) ;
241- }
242- }
243- }
244- }
245- all_props
214+ . collect ( )
246215}
247216
248- fn pretty_print_props ( props : & Vec < ( String , String ) > ) -> String {
217+ fn pretty_print_props ( props : & [ ( String , String ) ] ) -> String {
249218 let mut s = String :: new ( ) ;
250219 for ( k, v) in props {
251220 s. push_str ( & format ! ( "{}: {}, " , k, v) ) ;
@@ -258,17 +227,12 @@ fn pretty_print_given_props(props: Opts) -> String {
258227 for kv in props. pairs . iter ( ) {
259228 let key = kv. key . to_string ( ) ;
260229 let value = match & kv. value {
261- Expr :: Lit ( expr_lit) => {
262- if let syn:: Lit :: Str ( lit_str) = & expr_lit. lit {
263- lit_str. value ( )
264- } else if let syn:: Lit :: Bool ( lit_bool) = & expr_lit. lit {
265- lit_bool. value . to_string ( )
266- } else if let syn:: Lit :: Int ( lit_int) = & expr_lit. lit {
267- lit_int. base10_digits ( ) . to_string ( )
268- } else {
269- "unsupported" . to_string ( )
270- }
271- }
230+ Expr :: Lit ( v) => match & v. lit {
231+ Lit :: Str ( v) => v. value ( ) ,
232+ Lit :: Bool ( v) => v. value . to_string ( ) ,
233+ Lit :: Int ( v) => v. base10_digits ( ) . to_string ( ) ,
234+ _ => "unsupported" . to_string ( ) ,
235+ } ,
272236 _ => "unsupported" . to_string ( ) ,
273237 } ;
274238 s. push_str ( & format ! ( "{}: {}, " , key, value) ) ;
0 commit comments