securefmt 0.1.5

Drop-in replacement for the Debug derive macro that hides fields marked as sensitive.
Documentation
#[cfg(not(feature = "debug_mode"))]
use quote::format_ident;
use syn::Field;

use crate::formatting_data::FieldFormattingData;

impl From<&Field> for FieldFormattingData {
    #[cfg(not(feature = "debug_mode"))]
    fn from(field: &Field) -> Self {
        FieldFormattingData {
            ident: field.ident.as_ref().map(|it| it.to_string()),
            sensitive: field
                .attrs
                .iter()
                .any(|it| it.path().get_ident() == Some(&format_ident!("sensitive"))),
        }
    }

    #[cfg(feature = "debug_mode")]
    fn from(field: &Field) -> Self {
        FieldFormattingData {
            ident: field.ident.as_ref().map(std::string::ToString::to_string),
            sensitive: false,
        }
    }
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;
    use quote::format_ident;
    use syn::{
        parse_str,
        token::{Bracket, Not, Pound},
        AttrStyle, Attribute, Field, FieldMutability, Meta, Path, Type, TypePath, Visibility,
    };

    use crate::formatting_data::FieldFormattingData;

    #[test]
    fn should_contain_ident_for_named_field() {
        let field_type: TypePath = parse_str("u8").expect("String is not tokens");
        let field = Field {
            attrs: vec![],
            vis: Visibility::Inherited,
            ident: Some(format_ident!("TestField")),
            colon_token: None,
            ty: Type::Path(field_type),
            mutability: FieldMutability::None,
        };

        assert_eq!(
            FieldFormattingData::from(&field),
            FieldFormattingData {
                ident: Some("TestField".to_string()),
                sensitive: false
            }
        );
    }

    #[test]
    fn should_not_contain_ident_for_unnamed_field() {
        let field_type: TypePath = parse_str("u8").expect("String is not tokens");
        let field = Field {
            attrs: vec![],
            vis: Visibility::Inherited,
            ident: None,
            colon_token: None,
            ty: Type::Path(field_type),
            mutability: FieldMutability::None,
        };

        assert_eq!(
            FieldFormattingData::from(&field),
            FieldFormattingData {
                ident: None,
                sensitive: false
            }
        );
    }

    #[test]
    fn should_be_sensitive_when_field_has_sensitive_attribute() {
        let field_type: TypePath = parse_str("u8").expect("String is not tokens");
        let sensitive = Attribute {
            pound_token: Pound::default(),
            style: AttrStyle::Inner(Not::default()),
            bracket_token: Bracket::default(),
            meta: Meta::Path(Path::from(format_ident!("sensitive"))),
        };
        let field = Field {
            attrs: vec![sensitive],
            vis: Visibility::Inherited,
            ident: None,
            colon_token: None,
            ty: Type::Path(field_type),
            mutability: FieldMutability::None,
        };

        if cfg!(feature = "debug_mode") {
            assert_eq!(
                FieldFormattingData::from(&field),
                FieldFormattingData {
                    ident: None,
                    sensitive: false
                }
            );
        } else {
            assert_eq!(
                FieldFormattingData::from(&field),
                FieldFormattingData {
                    ident: None,
                    sensitive: true
                }
            );
        }
    }
}