1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
extern crate proc_macro;
use quote::quote;
use syn::parse::*;
use syn::*;
use uuid::Uuid;
#[proc_macro_derive(TypeUuid, attributes(uuid))]
pub fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let mut uuid = None;
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
let name_value = if let Meta::NameValue(name_value) = attribute {
name_value
} else {
continue;
};
if name_value
.path
.get_ident()
.map(|i| i != "uuid")
.unwrap_or(true)
{
continue;
}
let uuid_str = match name_value.lit {
Lit::Str(lit_str) => lit_str,
_ => panic!("uuid attribute must take the form `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"`"),
};
uuid = Some(
Uuid::parse_str(&uuid_str.value())
.expect("Value specified to `#[uuid]` attribute is not a valid UUID"),
);
}
let uuid =
uuid.expect("No `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"` attribute found");
let bytes = uuid
.as_bytes()
.iter()
.map(|byte| format!("{:#X}", byte))
.map(|byte_str| syn::parse_str::<LitInt>(&byte_str).unwrap());
let gen = quote! {
impl type_uuid::TypeUuid for #name {
const UUID: type_uuid::Bytes = [
#( #bytes ),*
];
}
};
gen.into()
}
struct ExternalDeriveInput {
path: ExprPath,
uuid_str: LitStr,
}
impl Parse for ExternalDeriveInput {
fn parse(input: ParseStream) -> Result<Self> {
let path = input.parse()?;
input.parse::<Token![,]>()?;
let uuid_str = input.parse()?;
Ok(Self { path, uuid_str })
}
}
#[proc_macro]
pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ExternalDeriveInput { path, uuid_str } = parse_macro_input!(tokens as ExternalDeriveInput);
let uuid = Uuid::parse_str(&uuid_str.value()).expect("Value was not a valid UUID");
let bytes = uuid
.as_bytes()
.iter()
.map(|byte| format!("{:#X}", byte))
.map(|byte_str| syn::parse_str::<LitInt>(&byte_str).unwrap());
let gen = quote! {
impl crate::TypeUuid for #path {
const UUID: crate::Bytes = [
#( #bytes ),*
];
}
};
gen.into()
}