use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, TokenStreamExt};
use syn::{parse2, spanned::Spanned, Ident, ItemFn, LitStr};
use crate::{
call::Call,
helpers::{add_span_to_signature, combine_cfg, CRATE_NAME},
precondition::{CfgPrecondition, Precondition, ReadWrite},
};
fn render_condition_list(mut preconditions: Vec<CfgPrecondition>, span: Span) -> TokenStream {
preconditions.sort_unstable();
let mut tokens = TokenStream::new();
let crate_name = Ident::new(&CRATE_NAME, span);
for precondition in preconditions {
match precondition.precondition() {
Precondition::ValidPtr {
ident, read_write, ..
} => {
let ident_lit = LitStr::new(&ident.to_string(), ident.span());
let rw_str = match read_write {
ReadWrite::Read { .. } => LitStr::new("r", read_write.span()),
ReadWrite::Write { .. } => LitStr::new("w", read_write.span()),
ReadWrite::Both { .. } => LitStr::new("r+w", read_write.span()),
};
tokens.append_all(quote_spanned! { precondition.span()=>
::#crate_name::ValidPtrCondition::<#ident_lit, #rw_str>
});
}
Precondition::ProperAlign { ident, .. } => {
let ident_lit = LitStr::new(&ident.to_string(), ident.span());
tokens.append_all(quote_spanned! { precondition.span()=>
::#crate_name::ProperAlignCondition::<#ident_lit>
});
}
Precondition::Boolean(expr) => {
let as_str = LitStr::new("e! { #expr }.to_string(), precondition.span());
tokens.append_all(quote_spanned! { precondition.span()=>
::#crate_name::BooleanCondition::<#as_str>
});
}
Precondition::Custom(string) => {
tokens.append_all(quote_spanned! { precondition.span()=>
::#crate_name::CustomCondition::<#string>
});
}
}
tokens.append_all(quote_spanned! { span=>
,
});
}
tokens
}
pub(crate) fn render_pre(
preconditions: Vec<CfgPrecondition>,
function: &mut ItemFn,
span: Span,
) -> TokenStream {
let combined_cfg = combine_cfg(&preconditions, span);
let preconditions = render_condition_list(preconditions, span);
add_span_to_signature(span, &mut function.sig);
function.sig.inputs.push(
parse2(quote_spanned! { span=>
#[cfg(all(not(doc), #combined_cfg))]
_: (#preconditions)
})
.expect("parses as a function argument"),
);
quote! {
#function
}
}
pub(crate) fn render_assure(
preconditions: Vec<CfgPrecondition>,
mut call: Call,
span: Span,
) -> Call {
let combined_cfg = combine_cfg(&preconditions, span);
let preconditions = render_condition_list(preconditions, span);
call.args_mut().push(
parse2(quote_spanned! { span=>
#[cfg(all(not(doc), #combined_cfg))]
(#preconditions)
})
.expect("parses as an expression"),
);
call
}