oasis_runtime_sdk_macros/
generators.rs1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::Ident;
4
5pub fn wrap_in_const(tokens: TokenStream) -> TokenStream {
9 quote! {
10 const _: () = {
11 #tokens
12 };
13 }
14}
15
16pub fn sdk_crate_path() -> syn::Path {
19 let is_internal = std::env::var("CARGO_PKG_NAME")
20 .map(|pkg_name| pkg_name == "oasis-runtime-sdk")
21 .unwrap_or_default();
22 if is_internal {
23 syn::parse_quote!(crate::oasis_runtime_sdk)
27 } else {
28 syn::parse_quote!(::oasis_runtime_sdk)
29 }
30}
31
32pub trait CodedVariant {
33 const FIELD_NAME: &'static str;
36
37 fn ident(&self) -> &Ident;
39
40 fn code(&self) -> Option<u32>;
42}
43
44pub fn enum_code_converter<V: CodedVariant>(
46 enum_binding: &Ident,
47 variants: &[&V],
48 autonumber: bool,
49) -> TokenStream {
50 if variants.is_empty() {
51 return quote!(0); }
53
54 let mut next_autonumber = 0u32;
55 let mut reserved_numbers = std::collections::BTreeSet::new();
56 let match_arms = variants.iter().map(|variant| {
57 let variant_ident = variant.ident();
58 let code = match variant.code() {
59 Some(code) => {
60 if reserved_numbers.contains(&code) {
61 variant_ident
62 .span()
63 .unwrap()
64 .error(format!("code {code} already used"))
65 .emit();
66 return quote!({});
67 }
68 reserved_numbers.insert(code);
69 code
70 }
71 None if autonumber => {
72 let mut reserved_successors = reserved_numbers.range(next_autonumber..);
73 while reserved_successors.next() == Some(&next_autonumber) {
74 next_autonumber += 1;
75 }
76 let code = next_autonumber;
77 reserved_numbers.insert(code);
78 next_autonumber += 1;
79 code
80 }
81 None => {
82 variant_ident
83 .span()
84 .unwrap()
85 .error(format!("missing `{}` for variant", V::FIELD_NAME))
86 .emit();
87 return quote!();
88 }
89 };
90 quote!(Self::#variant_ident { .. } => { #code })
91 });
92 quote! {
93 match #enum_binding {
94 #(#match_arms)*
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn generate_empty_enum_converter() {
105 struct DummyVariant {}
106 impl CodedVariant for DummyVariant {
107 const FIELD_NAME: &'static str = "code";
108 fn ident(&self) -> &Ident {
109 unimplemented!()
110 }
111 fn code(&self) -> Option<u32> {
112 unimplemented!()
113 }
114 }
115 let variants: &[&DummyVariant] = &[];
116
117 let expected: syn::Expr = syn::parse_quote!(0);
118 let converter = enum_code_converter("e::format_ident!("the_enum"), variants, false);
119 let actual: syn::Expr = syn::parse2(converter).unwrap();
120 assert_eq!(expected, actual);
121 }
122}