mod accessors;
mod diagnostics;
mod remote;
mod schema;
mod watch;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Error, ItemStruct, Result};
pub(crate) fn expand(mut input: ItemStruct) -> Result<TokenStream> {
let secrets = schema::take_field_options(&mut input)?;
let redacted_debug = diagnostics::expand_redacted_debug(&input, &secrets)?;
let name = &input.ident;
if let Some(lifetime) = input.generics.lifetimes().next() {
return Err(Error::new_spanned(
lifetime,
"`#[dynamic_config]` does not support lifetime parameters, because the \
configuration snapshot outlives every borrow that could name one",
));
}
let is_generic = !input.generics.params.is_empty();
let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
let where_clause = if is_generic {
let mut clause = where_clause
.cloned()
.unwrap_or_else(|| syn::parse_quote!(where));
clause
.predicates
.push(syn::parse_quote!(Self: 'static + ::core::marker::Send + ::core::marker::Sync));
quote!(#clause)
} else {
quote!(#where_clause)
};
let known_fields = schema::field_names(&input).unwrap_or_default();
let secret_names: Vec<String> = secrets.iter().map(|(_, name)| name.clone()).collect();
let cell_slot = accessors::cell_slot(is_generic, name, &type_generics);
let configured_slot = accessors::configured_slot(is_generic, name, &type_generics);
let defaults_slot = accessors::defaults_slot(is_generic);
let overrides_slot = accessors::overrides_slot(is_generic);
let remote_slot = accessors::remote_slot(is_generic);
let aliases_slot = accessors::aliases_slot(is_generic);
let bindings_slot = accessors::bindings_slot(is_generic);
let flags_slot = accessors::flags_slot(is_generic);
let layer_setters = accessors::layer_setters();
let introspection_methods = diagnostics::introspection_methods(&secret_names);
let hook_methods = watch::hook_methods();
let defaults_and_flag_setters = accessors::defaults_and_flag_setters();
let remote_methods = remote::remote_methods(name);
let binding_methods = accessors::binding_methods();
let clear_remote_method = remote::clear_remote_method();
let clear_flags_method = accessors::clear_flags_method();
let check_method = diagnostics::check_method();
let clear_layer_methods = accessors::clear_layer_methods();
Ok(quote! {
#input
#redacted_debug
impl #impl_generics #name #type_generics #where_clause {
#defaults_slot
#remote_slot
#aliases_slot
#bindings_slot
#flags_slot
#overrides_slot
const DYNAMIC_CONFIG_FIELDS: &'static [&'static str] = &[#(#known_fields),*];
#[must_use]
pub fn builder(key: &str) -> ::dynamic_config::Builder<Self> {
::dynamic_config::Builder::new(key)
.with_installer(Self::replace)
.with_secrets(&[#(#secret_names),*])
.with_fields(Self::DYNAMIC_CONFIG_FIELDS)
.with_type_statics(
Self::dynamic_config_defaults(),
Self::dynamic_config_overrides(),
Self::dynamic_config_flags(),
Self::dynamic_config_env_bindings(),
Self::dynamic_config_aliases(),
Self::dynamic_config_remote(),
Self::dynamic_config_remember,
)
}
fn dynamic_config_remember(builder: &::dynamic_config::Builder<Self>) {
Self::dynamic_config_configured().set(::core::clone::Clone::clone(builder));
}
fn dynamic_config_builder(
) -> ::core::result::Result<::dynamic_config::Builder<Self>, ::dynamic_config::Error>
{
Self::dynamic_config_configured().get(::core::stringify!(#name))
}
#layer_setters
#introspection_methods
#hook_methods
#defaults_and_flag_setters
#remote_methods
#binding_methods
#clear_remote_method
#clear_flags_method
#check_method
#clear_layer_methods
#cell_slot
#configured_slot
pub fn prepare() -> ::core::result::Result<
::dynamic_config::Commit,
::dynamic_config::Error,
> {
Self::dynamic_config_builder()?.prepare()
}
pub fn replace(config: Self) {
Self::dynamic_config_cell().store(config);
}
pub fn current() -> ::std::sync::Arc<Self> {
Self::dynamic_config_cell().get_or_panic(::core::stringify!(#name))
}
pub fn try_current() -> ::core::option::Option<::std::sync::Arc<Self>> {
Self::dynamic_config_cell().load()
}
::dynamic_config::__clap_methods!();
::dynamic_config::__async_methods!(#name);
::dynamic_config::__async_remote_methods!();
}
impl #impl_generics ::dynamic_config::Reloadable for #name #type_generics
#where_clause
{
fn prepare() -> ::core::result::Result<
::dynamic_config::Commit,
::dynamic_config::Error,
> {
Self::prepare()
}
fn name() -> &'static str {
::core::stringify!(#name)
}
}
})
}