1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{quote, quote_spanned};
4use syn::spanned::Spanned;
5
6#[proc_macro_derive(TypeUlid, attributes(ulid))]
16pub fn type_ulid(input: TokenStream) -> TokenStream {
17 let input = syn::parse(input).unwrap();
18
19 impl_type_ulid(&input).into()
20}
21
22fn impl_type_ulid(input: &syn::DeriveInput) -> TokenStream2 {
23 let item_ident = &input.ident;
24
25 let mut ulid = None;
27 for attr in &input.attrs {
28 let Ok(syn::Meta::NameValue(name_value)) = attr.parse_meta() else {
29 continue;
30 };
31
32 if name_value
33 .path
34 .get_ident()
35 .map(|i| i != "ulid")
36 .unwrap_or(true)
37 {
38 continue;
39 }
40
41 let syn::Lit::Str(lit_str) = name_value.lit else {
42 continue;
43 };
44
45 match ulid::Ulid::from_string(&lit_str.value()) {
46 Ok(id) => ulid = Some(id),
47 Err(e) => {
48 let msg = e.to_string();
49 return quote_spanned! { attr.span() =>
50 compile_error!(concat!("Could not parse ULID: ", #msg));
51 };
52 }
53 }
54 }
55
56 let Some(ulid) = ulid else {
57 return quote! {
58 compile_error!("You must specify a `ulid` attribute");
59 };
60 };
61
62 let id = ulid.0;
64 quote! {
65 impl ::type_ulid::TypeUlid for #item_ident {
66 const ULID: ::type_ulid::Ulid = ::type_ulid::Ulid(#id);
67 }
68 }
69}