valence_ident_macros/
lib.rs
1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream as StdTokenStream;
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::{parse2, Error, LitStr, Result};
7
8#[proc_macro]
9pub fn parse_ident_str(item: StdTokenStream) -> StdTokenStream {
10 parse_ident_str_inner(item.into())
11 .unwrap_or_else(Error::into_compile_error)
12 .into()
13}
14
15fn parse_ident_str_inner(item: TokenStream) -> Result<TokenStream> {
16 let ident_lit: LitStr = parse2(item)?;
17 let mut ident = ident_lit.value();
18
19 match ident.split_once(':') {
20 Some((namespace, path)) if check_namespace(namespace) && check_path(path) => {}
21 None if check_path(&ident) => {
22 ident = format!("minecraft:{ident}");
23 }
24 _ => {
25 return Err(syn::Error::new(
26 ident_lit.span(),
27 "string cannot be parsed as a resource identifier",
28 ));
29 }
30 }
31
32 Ok(quote!(#ident))
33}
34
35fn check_namespace(s: &str) -> bool {
36 !s.is_empty()
37 && s.chars()
38 .all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '.' | '-'))
39}
40
41fn check_path(s: &str) -> bool {
42 !s.is_empty()
43 && s.chars()
44 .all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '.' | '-' | '/'))
45}