Field attributes

attributes that can be applied to a struct field, enum variant or field in an enum variant

as

Provide a value that the given field/variant should be set to when expunged. e.g. "<expunged>".to_string()

use expunge::Expunge;

#[derive(Expunge)]
#[expunge(as = "<redacted>".to_string())]
struct ConnectionInfo {
  username: String,
  password: String,
  host: String,
}

default

Shorthand for as = Default::default()

Example:

use expunge::Expunge;

#[derive(Default)]
struct Location(f64, f64);

#[derive(Expunge)]
struct UserData {
  username: String,
  password: String,
  #[expunge(default)]
  location: Location,
}

with

Expunge the field/variant using this function.

It must return the same type as it takes. e.g. hash a String with sha256::digest

If you own the type, then could also implement Expunge directly. Using with, however, allows you to use different transformations for different fields of the same type.

Example:

use expunge::Expunge;

fn redact_first_char(mut s: String) -> String {
    s.replace_range(0..1, "*");
    s
}

fn char_count_of(s: String) -> String{
    s.len().to_string()
}

#[derive(Expunge)]
#[cfg_attr(test, derive(Eq, PartialEq, Debug), expunge(allow_debug))]
struct User {
  username: String,
  #[expunge(with = char_count_of)]
  first_name: String,
  #[expunge(with = redact_first_char)]
  last_name: String,
  #[expunge(with = sha256::digest)]
  password: String,
}

#[test]
fn field_with() {
    let user = User {
        username: "some_user_123".to_string(),
        first_name: "Jane".to_string(),
        last_name: "Doe".to_string(),
        password: "password123".to_string(),
    };

    assert_eq!(User{
        username: "".to_string(),
        first_name: "4".to_string(),
        last_name: "*oe".to_string(),
        password: "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f".to_string(),
    }, user.expunge());
}

skip

Skips a field. Fields marked skip will be left as-is. This is useful when:

  1. You want to preserve fields within a struct that are not sensitive
  2. The type cannot be expunged in a meaningful way
use expunge::Expunge;

#[derive(Expunge)]
struct UserLogin {
  username: String,
  password: String,
  #[expunge(skip)]
  last_logged_in_at: i64, // the last login timestamp will be left as-is
}

#[test]
fn skip() {
    let login = UserLogin{
        username: "gamer100".to_string(),
        password: "somepassword123".to_string(),
        last_logged_in_at: 1716113380,
    };

    let expunged = login.expunge();
    assert_eq!("", expunged.username);
    assert_eq!("", expunged.password);
    assert_eq!(1716113380, expunged.last_logged_in_at);
}

zeroize

Zeroize memory for extra security via the secrecy & zeroize crates.

Example:

use expunge::Expunge;

#[derive(Expunge)]
struct UserLogin {
  username: String,
  #[expunge(as = "<redacted>".to_string(), zeroize)]
  password: String, // password will be scrubbed from memory after expunging
}