This repository has been archived on 2024-07-25. You can view files and clone it, but cannot push or open issues or pull requests.
enve/itconfig-macro/src/expand.rs

204 lines
5.5 KiB
Rust
Raw Normal View History

2020-03-12 23:20:34 +03:00
use crate::ast::*;
use proc_macro2::TokenStream as TokenStream2;
2020-07-02 20:54:17 +03:00
use quote::{quote, ToTokens, TokenStreamExt};
use syn::Path;
use syn::Type;
2020-03-12 23:20:34 +03:00
fn vec_to_token_stream_2<T>(input: &Vec<T>) -> Vec<TokenStream2>
2020-07-02 20:54:17 +03:00
where
T: ToTokens,
2020-03-12 23:20:34 +03:00
{
2020-07-02 20:54:17 +03:00
input.iter().map(|ns| ns.into_token_stream()).collect()
2020-03-12 23:20:34 +03:00
}
impl ToTokens for RootNamespace {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let name = &self.name;
let variables = vec_to_token_stream_2(&self.variables);
let namespaces = vec_to_token_stream_2(&self.namespaces);
2020-07-02 20:54:17 +03:00
let init_variables = self
.variables
.iter()
2020-03-12 23:20:34 +03:00
.map(|var| {
let name = &var.name;
let var_meta = vec_to_token_stream_2(&var.meta);
quote!(
#(#var_meta)*
#name();
)
})
.collect::<Vec<TokenStream2>>();
2020-07-02 20:54:17 +03:00
let init_namespaces = self
.namespaces
.iter()
2020-03-12 23:20:34 +03:00
.map(|ns| {
let name = &ns.name;
let ns_meta = vec_to_token_stream_2(&ns.meta);
quote!(
#(#ns_meta)*
#name::init();
)
})
.collect::<Vec<TokenStream2>>();
let inner_meta: Vec<TokenStream2> = if name.is_none() {
vec![]
2020-07-02 20:54:17 +03:00
} else if self.meta.is_empty() {
2020-03-12 23:20:34 +03:00
vec![quote!(#![allow(non_snake_case)])]
} else {
vec_to_token_stream_2(&self.meta)
};
let inner_rules = quote! {
#(#inner_meta)*
#(#namespaces)*
#(#variables)*
pub fn init() {
#(#init_variables)*
#(#init_namespaces)*
}
};
2020-07-02 20:54:17 +03:00
tokens.append_all(match self.name.as_ref() {
None => inner_rules,
Some(name) => quote! {
pub mod #name {
#inner_rules
2020-03-12 23:20:34 +03:00
}
2020-07-02 20:54:17 +03:00
},
});
2020-03-12 23:20:34 +03:00
}
}
impl ToTokens for Namespace {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let name = &self.name;
let variables = vec_to_token_stream_2(&self.variables);
let namespaces = vec_to_token_stream_2(&self.namespaces);
let meta = vec_to_token_stream_2(&self.meta);
2020-07-02 20:54:17 +03:00
let init_variables = self
.variables
.iter()
2020-03-12 23:20:34 +03:00
.map(|var| {
let name = &var.name;
let var_meta = vec_to_token_stream_2(&var.meta);
quote!(
#(#var_meta)*
#name();
)
})
.collect::<Vec<TokenStream2>>();
2020-07-02 20:54:17 +03:00
let init_namespaces = self
.namespaces
.iter()
2020-03-12 23:20:34 +03:00
.map(|ns| {
let name = &ns.name;
let ns_meta = vec_to_token_stream_2(&ns.meta);
quote!(
#(#ns_meta)*
#name::init();
)
})
.collect::<Vec<TokenStream2>>();
tokens.append_all(quote!(
#(#meta)*
pub mod #name {
#(#namespaces)*
#(#variables)*
pub fn init() {
#(#init_variables)*
#(#init_namespaces)*
}
}
))
}
}
impl ToTokens for Variable {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let ty = &self.ty;
let name = &self.name;
2020-07-02 20:54:17 +03:00
let env_name = &self
.env_name
.clone()
2020-03-12 23:20:34 +03:00
.unwrap_or(name.to_string().to_uppercase());
let meta = vec_to_token_stream_2(&self.meta);
let get_variable: TokenStream2 = if self.concat_parts.is_some() {
let concat_parts = self.concat_parts.as_ref().unwrap();
quote! {{
let value_parts: Vec<String> = vec!(#(#concat_parts),*);
let value = value_parts.join("");
::std::env::set_var(#env_name, value.as_str());
value
}}
} else if self.initial.is_some() {
let initial = self.initial.as_ref().unwrap();
quote!(::itconfig::get_env_or_set_default(#env_name, #initial))
} else if is_option_type(&self.ty) {
quote!(::itconfig::maybe_get_env(#env_name))
2020-03-12 23:20:34 +03:00
} else {
quote!(::itconfig::get_env_or_panic(#env_name))
};
if self.is_static {
tokens.append_all(quote!(
#(#meta)*
pub fn #name() -> #ty {
::lazy_static::lazy_static! {
static ref #name: #ty = #get_variable;
}
(*#name).clone()
}
));
} else {
tokens.append_all(quote!(
#(#meta)*
pub fn #name() -> #ty {
#get_variable
}
));
}
}
2020-07-02 20:54:17 +03:00
}
fn path_ident(path: &Path) -> String {
path.segments
.iter()
.into_iter()
.fold(String::with_capacity(250), |mut acc, v| {
acc.push_str(&v.ident.to_string());
acc.push('|');
acc
})
}
fn is_option_path_ident(path_ident: String) -> bool {
vec!["Option|", "std|option|Option|", "core|option|Option|"]
.into_iter()
.find(|s| &path_ident == *s)
.is_some()
}
fn is_option_type(ty: &Type) -> bool {
match ty {
Type::Path(ty_path) => {
ty_path.qself.is_none() && is_option_path_ident(path_ident(&ty_path.path))
}
_ => false,
}
}