use proc_macro::{TokenStream, TokenTree, Delimiter};
use crate::{errors::NSCFGError, config::{DOC_ALIAS, is_nscfg_autodoc, if_docsrs_enabled}, syntax::{SyntaxTreeNode, AND_SYMBOL, OR_SYMBOL, NEGATIVE_SYMBOL}, NscfgMacroSource};
#[allow(unused_imports)]
use crate::config::{get_release_modifier_behaviour, ReleaseModifierBehaviour};
pub(crate) const ARM_SEPARATOR : char = ',';
pub(crate) const CONTENT_SEPARATOR_0 : char = '=';
pub(crate) const CONTENT_SEPARATOR_1 : char = '>';
pub(crate) const WILDCARD_ARM : char = '_';
pub(crate) const WILDCARD_ARM_STR : &str = "_";
pub(crate) const LEGACY_ARM: char = '#';
pub(crate) const MODIFIER_ACTIVATE: char = '+';
pub(crate) const MODIFIER_ACTIVATE_VALUE: &str = "all()";
pub(crate) const MODIFIER_DEACTIVATE: char = '-';
pub(crate) const MODIFIER_DEACTIVATE_VALUE: &str = "any()";
pub(crate) const MODIFIER_PANIC: char = '@';
#[derive(Debug, Clone, Copy)]
pub(crate) enum TargetArmType {
Simplified,
Legacy,
Wildcard,
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum TargetArmModifier {
None,
Activate,
Deactivate,
Panic,
}
#[derive(Clone)]
pub(crate) struct TargetArm {
pub arm_type : TargetArmType, pub modifier : TargetArmModifier, pub arm_ts : TokenStream, pub pred_ts : TokenStream, pub cfg_ts : TokenStream, pub attr_ts : TokenStream, pub content : TokenStream, }
impl ToString for TargetArm {
fn to_string(&self) -> String {
format!("\nArm : {}\nSyntax : {:?}\nModifier : {:?}\nPredicates : {}\n#[cfg()] : {}\n#[cfg_attr()] : {}\nContent : {}\n",
self.arm_ts.to_string(), self.arm_type, self.modifier, self.pred_ts.to_string(), self.cfg_ts.to_string(), self.attr_ts.to_string(), self.content.to_string())
}
}
impl TargetArm {
pub fn new() -> TargetArm {
TargetArm { arm_type : TargetArmType::Simplified, modifier:TargetArmModifier::None, arm_ts : TokenStream::new(), pred_ts : TokenStream::new(), cfg_ts : TokenStream::new(), attr_ts : TokenStream::new(), content : TokenStream::new() }
}
pub fn extract(source : TokenStream, macro_src : NscfgMacroSource) -> Vec<TargetArm> {
let mut arms : Vec<TargetArm> = Vec::new();
let mut arm = TargetArm::new();
let mut left_side = true;
let mut separator = false;
for token in source {
if !Self::handle_arm_separator(&mut arm, token.clone(), &mut left_side, &mut separator) {
if left_side { Self::extract_attributes(&mut arm, token);
} else { Self::extract_content(&mut arm, &mut arms, token, &mut left_side);
}
}
}
if !left_side {
Self::add_arm(&mut arms, &mut arm, &mut left_side);
}
Self::verify_arms_integrity(macro_src, &mut arms);
Self::generate_arms_predicate(macro_src, &mut arms);
Self::panic_arms(&arms);
arms
}
#[inline(always)]
fn panic_arms(arms : &Vec<TargetArm>) {
let arms = arms.iter().filter(|arm| match arm.modifier {
TargetArmModifier::Panic => true,
_ => false,
});
let mut message = String::new();
for arm in arms {
message.push_str(&arm.to_string());
}
if message.len() > 0 {
panic!("\n*** Macro panicked because some arm have the `{}` modifier ***\n{}", MODIFIER_PANIC, message);
}
}
#[inline(always)]
fn extract_attributes(arm : &mut TargetArm, token : TokenTree) {
match token.clone() {
TokenTree::Group(grp) => {
match arm.arm_type { TargetArmType::Legacy => match grp.delimiter() {
Delimiter::Bracket => {},
_ => panic!("{}", NSCFGError::LegacySyntaxError.message(token.to_string().as_str())), },
_ => {},
}
arm.arm_ts.extend(TokenStream::from(token));
},
TokenTree::Ident(ident) => match ident.to_string().as_str() { WILDCARD_ARM_STR => {
if arm.arm_ts.is_empty() { arm.arm_type = TargetArmType::Wildcard;
} else {
arm.arm_ts.extend(TokenStream::from(token));
}
},
_ => arm.arm_ts.extend(TokenStream::from(token)),
},
TokenTree::Punct(punct) => match punct.as_char() { MODIFIER_PANIC => {
if !arm.arm_ts.is_empty() {
panic!("{}", NSCFGError::ModifierNotFirst.message("")); }
arm.modifier = TargetArmModifier::Panic;
},
MODIFIER_ACTIVATE => {
if !arm.arm_ts.is_empty() {
panic!("{}", NSCFGError::ModifierNotFirst.message("")); }
#[cfg(debug_assertions)]
{
arm.modifier = TargetArmModifier::Activate;
}
#[cfg(not(debug_assertions))]
{
match get_release_modifier_behaviour() {
ReleaseModifierBehaviour::Panic => panic!("{}", NSCFGError::ModifierPanicRelease.message("")), _ => {}, }
}
},
MODIFIER_DEACTIVATE => {
if !arm.arm_ts.is_empty() {
panic!("{}", NSCFGError::ModifierNotFirst.message("")); }
#[cfg(debug_assertions)]
{
arm.modifier = TargetArmModifier::Deactivate;
}
#[cfg(not(debug_assertions))]
{
match get_release_modifier_behaviour() {
ReleaseModifierBehaviour::Panic => panic!("{}", NSCFGError::ModifierPanicRelease.message("")), _ => {}, }
}
},
LEGACY_ARM => {
if !arm.arm_ts.is_empty() {
panic!("{}", NSCFGError::MixedSyntaxError.message("")); }
arm.arm_type = TargetArmType::Legacy;
arm.arm_ts.extend(TokenStream::from(token));
},
NEGATIVE_SYMBOL | AND_SYMBOL | OR_SYMBOL => { match arm.arm_type{
TargetArmType::Legacy => panic!("{}", NSCFGError::MixedSyntaxError.message("")), _ => arm.arm_ts.extend(TokenStream::from(token)),
}
},
ARM_SEPARATOR => panic!("{}", NSCFGError::ContentSeparatorMissing.message("")), CONTENT_SEPARATOR_0 | CONTENT_SEPARATOR_1 => {}, _ => arm.arm_ts.extend(TokenStream::from(token)),
},
_ => arm.arm_ts.extend(TokenStream::from(token)), }
}
#[inline(always)]
fn extract_content(arm : &mut TargetArm, arms : &mut Vec<TargetArm>, token : TokenTree, left_side : &mut bool) {
match token.clone() {
TokenTree::Group(grp) => arm.content.extend(match grp.delimiter() {
proc_macro::Delimiter::Brace => {
if arm.content.is_empty() { grp.stream() } else {
TokenStream::from(token) }
},
_ => TokenStream::from(token), }),
TokenTree::Punct(punct) => match punct.as_char() {
ARM_SEPARATOR => Self::add_arm(arms, arm, left_side), _ => arm.content.extend(TokenStream::from(token)), },
_ => arm.content.extend(TokenStream::from(token)),
}
}
#[inline(always)]
fn extract_legacy_predicates(legacy : TokenStream) -> TokenStream {
for token in legacy.clone() {
match token {
TokenTree::Group(grp) => match grp.delimiter() {
Delimiter::Parenthesis => return grp.stream(),
Delimiter::Bracket => return Self::extract_legacy_predicates(grp.stream()),
_ => {},
},
_ => {}
}
}
panic!("{}", NSCFGError::LegacySyntaxError.message(legacy.to_string().as_str()));
}
#[inline(always)]
fn handle_arm_separator(arm : &mut TargetArm, token : TokenTree, left_side : &mut bool, separator : &mut bool) -> bool {
match token.clone() {
TokenTree::Punct(punct) => {
match punct.as_char(){
CONTENT_SEPARATOR_0 => {
if *separator && *left_side { panic!("{}", NSCFGError::ContentSeparatorError.message(""));
} else {
*separator = true;
}
},
CONTENT_SEPARATOR_1 => {
if *separator {
*left_side = !*left_side; *separator = false;
if *left_side && !arm.content.is_empty() { panic!("{}", NSCFGError::ArmSeparatorMissing.message(""));
}
return true;
} else {
if *left_side { panic!("{}", NSCFGError::ContentSeparatorError.message(""));
}
}
},
_ => *separator = false, }
},
_ => *separator = false }
false
}
#[inline(always)]
fn add_arm(arms : &mut Vec<TargetArm>, arm : &mut TargetArm, left_side : &mut bool){
arms.push(arm.clone());
*arm = TargetArm::new();
*left_side = true;
}
#[inline(always)]
fn generate_arms_predicate(macro_src : NscfgMacroSource, arms : &mut Vec<TargetArm>){
match macro_src {
NscfgMacroSource::TargetMacro => {
arms.iter_mut().for_each(|arm| {
arm.pred_ts.extend(Self::generate_pred_ts(arm.arm_type, arm.arm_ts.clone()));
arm.cfg_ts.extend(Self::generate_target_cfg_ts(Self::set_default_doc(arm.pred_ts.clone()), arm.modifier));
arm.attr_ts.extend(Self::generate_target_attr_ts(arm.pred_ts.clone()));
});
},
NscfgMacroSource::MatchMacro => {
#[cfg(debug_assertions)]
{
if arms.iter().filter(|arm| match arm.modifier{
TargetArmModifier::Activate => true,
_ => false,
}).count() > 0 {
arms.iter_mut().for_each(|arm| match arm.modifier {
TargetArmModifier::Activate => {},
_ => arm.modifier = TargetArmModifier::Deactivate ,
});
}
}
let mut cumul_ts = MODIFIER_ACTIVATE_VALUE.parse::<TokenStream>().unwrap();
arms.iter_mut().for_each(|arm| {
arm.pred_ts.extend(Self::generate_pred_ts(arm.arm_type, arm.arm_ts.clone()));
let pred_ts = format!("all({},{})", cumul_ts, arm.pred_ts.clone()).parse::<TokenStream>().unwrap();
arm.cfg_ts.extend(Self::generate_target_cfg_ts(pred_ts.clone(), arm.modifier));
cumul_ts.extend(format!(", not({})", { #[cfg(debug_assertions)]
{
match arm.modifier {
TargetArmModifier::Activate => MODIFIER_ACTIVATE_VALUE.parse::<TokenStream>().unwrap(),
TargetArmModifier::Deactivate => MODIFIER_DEACTIVATE_VALUE.parse::<TokenStream>().unwrap(),
_ => arm.pred_ts.clone(),
}
}
#[cfg(not(debug_assertions))]
{
arm.pred_ts.clone()
}
}).parse::<TokenStream>().unwrap());
});
},
}
}
#[inline(always)]
fn generate_target_cfg_ts(pred_ts : TokenStream, modifier : TargetArmModifier) -> TokenStream {
let mut pred_str = pred_ts.to_string();
#[cfg(debug_assertions)]
{
match modifier{
TargetArmModifier::Activate => pred_str = String::from(MODIFIER_ACTIVATE_VALUE),
TargetArmModifier::Deactivate => pred_str = String::from(MODIFIER_DEACTIVATE_VALUE),
_ => {}
}
}
format!("#[cfg({})]", pred_str).parse::<TokenStream>().unwrap()
}
#[inline(always)]
fn generate_pred_ts(arm_type : TargetArmType, arm_ts : TokenStream) -> TokenStream {
match arm_type{
TargetArmType::Simplified => {
let syntax_tree = SyntaxTreeNode::generate(arm_ts); syntax_tree.to_string().parse::<TokenStream>().unwrap()
},
TargetArmType::Legacy => {
Self::extract_legacy_predicates(arm_ts)
},
TargetArmType::Wildcard => MODIFIER_ACTIVATE_VALUE.parse::<TokenStream>().unwrap(), }
}
#[inline(always)]
fn generate_target_attr_ts(pred_ts : TokenStream) -> TokenStream {
if if_docsrs_enabled() { format!("#[cfg_attr(docsrs, doc(cfg({})))]", pred_ts.to_string()).parse::<TokenStream>().unwrap()
} else {
TokenStream::new()
}
}
#[inline(always)]
fn set_default_doc(pred_ts : TokenStream) -> TokenStream {
if is_nscfg_autodoc() {
if Self::is_set_attr_autodoc(pred_ts.clone()) { pred_ts
} else {
format!("any({}, {})", DOC_ALIAS, pred_ts).parse::<TokenStream>().unwrap()
}
} else { pred_ts
}
}
#[inline(always)]
fn is_set_attr_autodoc(attr : TokenStream) -> bool {
for token in attr.clone() {
match token.clone() {
TokenTree::Ident(ident) => {
if ident.to_string().as_str().eq(DOC_ALIAS) {
return true;
}
},
TokenTree::Group(grp) => return Self::is_set_attr_autodoc(grp.stream()),
_ => {}
}
}
false
}
#[inline(always)]
fn verify_arms_integrity(macro_src : NscfgMacroSource, arms: &mut Vec<TargetArm>) {
match macro_src {
NscfgMacroSource::TargetMacro => {
if Self::has_wild_arm(&arms){ panic!("{}", NSCFGError::WildcardArmOnTarget.message(""));
}
for arm in arms {
if Self::is_inside_function(arm) {
panic!("{}", NSCFGError::TargetInFunction.message(""));
}
if arm.arm_ts.is_empty() { panic!("{}", NSCFGError::EmptyArm.message(""));
}
}
},
_ => {
if !Self::has_wild_arm(&arms){ panic!("{}", NSCFGError::WildcardArmMissing.message(""));
}
let mut activated:usize = 0;
arms.iter().for_each(|arm| match arm.modifier {
TargetArmModifier::Activate => activated += 1, TargetArmModifier::Deactivate => match arm.arm_type{
TargetArmType::Wildcard => panic!("{}", NSCFGError::MatchDeactivatedWildArm.message("")), _ => {},
},
_ => {},
});
if activated > 1 { panic!("{}", NSCFGError::MatchModifierMoreThanOneActivate.message(""));
}
}
}
}
#[inline(always)]
fn is_inside_function(arm: &TargetArm) -> bool {
for t in arm.content.clone() {
match t {
proc_macro::TokenTree::Ident(ident) => match ident.to_string().as_str() {
"let" | "if" | "else" | "loop" | "break" | "while" | "for" | "match" | "println" | "panic" => return true, _ => {},
},
_ => {},
}
}
false
}
#[inline(always)]
fn has_wild_arm(arms : &Vec<TargetArm>) -> bool {
let mut has_wc = false;
for arm in arms {
if has_wc { panic!("{}", NSCFGError::WildcardArmNotLast.message(""));
}
match arm.arm_type {
TargetArmType::Wildcard => has_wc = true,
_ => {},
}
}
has_wc
}
}