use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use crate::errors::Result;
use crate::implementation::FmtBodySource;
pub fn debug(ident: &Ident, fmt_body_source: &impl FmtBodySource) -> Result<TokenStream> {
let fmt_body = fmt_body_source.generate_fmt_body(&ident.to_string())?;
let once_ident = format_ident!("WARN_ONCE_{}", ident.to_string().to_uppercase());
let warning = warning(&once_ident);
Ok(quote! {
static #once_ident: std::sync::Once = std::sync::Once::new();
impl core::fmt::Debug for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#warning
#fmt_body
}
}
})
}
#[cfg(feature = "debug_mode")]
fn warning(once_ident: &Ident) -> TokenStream {
quote! {
#once_ident.call_once(|| {
log::warn!("WARNING: securefmt debug_mode feature is active. Sensitive data may be leaked. It is strongly recommended to disable debug_mode in production releases.");
});
}
}
#[cfg(not(feature = "debug_mode"))]
fn warning(_: &Ident) -> TokenStream {
TokenStream::new()
}
#[cfg(test)]
mod tests {
use mockall::predicate;
use pretty_assertions::assert_eq;
use quote::{format_ident, quote};
use crate::implementation::*;
#[test]
#[cfg_attr(feature = "debug_mode", ignore)]
fn should_generate_debug_implementation_without_warning_when_debug_mode_is_inactive() {
let ident = format_ident!("TestStruct");
let mut formatting_data = MockFmtBodySource::new();
formatting_data
.expect_generate_fmt_body()
.with(predicate::eq("TestStruct"))
.returning(|_| Ok(quote! { FMTBODY }));
assert_eq!(
super::debug(&ident, &formatting_data)
.expect("Should have succeeded")
.to_string(),
quote!(
static WARN_ONCE_TESTSTRUCT: std::sync::Once = std::sync::Once::new();
impl core::fmt::Debug for TestStruct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FMTBODY
}
}
)
.to_string()
);
}
#[test]
#[cfg(feature = "debug_mode")]
fn should_generate_debug_implementation_with_warning_when_debug_mode_is_active() {
let ident = format_ident!("TestStruct");
let mut formatting_data = MockFmtBodySource::new();
formatting_data
.expect_generate_fmt_body()
.with(predicate::eq("TestStruct"))
.returning(|_| Ok(quote! { FMTBODY }));
assert_eq!(
super::debug(&ident, &formatting_data).expect("Should have succeeded").to_string(),
quote!(
static WARN_ONCE_TESTSTRUCT: std::sync::Once = std::sync::Once::new();
impl core::fmt::Debug for TestStruct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
WARN_ONCE_TESTSTRUCT.call_once(|| {
log::warn!("WARNING: securefmt debug_mode feature is active. Sensitive data may be leaked. It is strongly recommended to disable debug_mode in production releases.");
});
FMTBODY
}
}
).to_string()
);
}
}