forked from NationTech/harmony
feat: WIP add secrets module and macro crate
This commit is contained in:
100
harmony_secrets_derive/src/lib.rsglm45
Normal file
100
harmony_secrets_derive/src/lib.rsglm45
Normal file
@@ -0,0 +1,100 @@
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, Attribute, Meta};
|
||||
use quote::quote;
|
||||
use proc_macro_crate::crate_name;
|
||||
|
||||
#[proc_macro_derive(Secret, attributes(secret))]
|
||||
pub fn derive_secret(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
// Verify this is a unit struct
|
||||
if !matches!(&input.data, syn::Data::Struct(data) if data.fields.is_empty()) {
|
||||
return syn::Error::new_spanned(
|
||||
input.ident,
|
||||
"#[derive(Secret)] only supports unit structs (e.g., `struct MySecret;`)",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
// Parse the #[secret(...)] attribute
|
||||
let (namespace, key, value_type) = match parse_secret_attributes(&input.attrs) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
|
||||
// Get the path to the harmony_secrets crate
|
||||
let secret_crate_path = match crate_name("harmony-secrets") {
|
||||
Ok(proc_macro_crate::FoundCrate::Itself) => quote!(crate),
|
||||
Ok(proc_macro_crate::FoundCrate::Name(name)) => {
|
||||
let ident = quote::format_ident!("{}", name);
|
||||
quote!(::#ident)
|
||||
}
|
||||
Err(_) => {
|
||||
return syn::Error::new_spanned(
|
||||
&input.ident,
|
||||
"harmony-secrets crate not found in dependencies",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
let struct_ident = input.ident;
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl #secret_crate_path::Secret for #struct_ident {
|
||||
type Value = #value_type;
|
||||
const NAMESPACE: &'static str = #namespace;
|
||||
const KEY: &'static str = #key;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_secret_attributes(attrs: &[Attribute]) -> syn::Result<(String, String, syn::Type)> {
|
||||
let secret_attr = attrs
|
||||
.iter()
|
||||
.find(|attr| attr.path().is_ident("secret"))
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new_spanned(
|
||||
attrs.first().unwrap_or_else(|| &attrs[0]),
|
||||
"missing #[secret(...)] attribute",
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut namespace = None;
|
||||
let mut key = None;
|
||||
let mut value_type = None;
|
||||
|
||||
if let Meta::List(meta_list) = &secret_attr.parse_meta()? {
|
||||
for nested in &meta_list.nested {
|
||||
if let syn::NestedMeta::Meta(Meta::NameValue(nv)) = nested {
|
||||
if nv.path.is_ident("namespace") {
|
||||
if let syn::Lit::Str(lit) = &nv.lit {
|
||||
namespace = Some(lit.value());
|
||||
}
|
||||
} else if nv.path.is_ident("key") {
|
||||
if let syn::Lit::Str(lit) = &nv.lit {
|
||||
key = Some(lit.value());
|
||||
}
|
||||
} else if nv.path.is_ident("value_type") {
|
||||
if let syn::Lit::Str(lit) = &nv.lit {
|
||||
value_type = Some(syn::parse_str::<syn::Type>(&lit.value())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
namespace.ok_or_else(|| {
|
||||
syn::Error::new_spanned(secret_attr, "missing `namespace` in #[secret(...)]")
|
||||
})?,
|
||||
key.ok_or_else(|| {
|
||||
syn::Error::new_spanned(secret_attr, "missing `key` in #[secret(...)]")
|
||||
})?,
|
||||
value_type.ok_or_else(|| {
|
||||
syn::Error::new_spanned(secret_attr, "missing `value_type` in #[secret(...)]")
|
||||
})?,
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user