use crate::models::thing::Thing;
use serde_json::Value;
pub fn mask_thing(thing: &Thing) -> Thing {
let mut masked = thing.clone();
if masked.owner.is_some() {
masked.owner = Some("[owner withheld]".to_string());
}
for id in masked.identifiers.iter_mut() {
id.value = mask_value(&id.value);
id.url = None;
}
masked
}
fn mask_value(v: &str) -> String {
if v.len() <= 4 {
return "****".to_string();
}
let tail = &v[v.len() - 4..];
format!("****{tail}")
}
pub fn gdpr_export(thing: &Thing) -> Value {
serde_json::to_value(thing).unwrap_or(Value::Null)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::identifier::ThingIdentifier;
#[test]
fn test_mask_owner() {
let mut thing = Thing::new("Private Diary");
thing.owner = Some("Jane Doe".into());
let masked = mask_thing(&thing);
assert_eq!(masked.owner.as_deref(), Some("[owner withheld]"));
}
#[test]
fn test_mask_identifier_value() {
let mut thing = Thing::new("Test");
thing.identifiers = vec![ThingIdentifier::serial_number("ABCD1234567890")];
let masked = mask_thing(&thing);
let v = &masked.identifiers[0].value;
assert!(v.starts_with("****"));
assert!(v.ends_with("7890"));
}
#[test]
fn test_mask_identifier_url_cleared() {
let mut thing = Thing::new("Test");
let mut id = ThingIdentifier::isbn("9780141439518");
id.url = Some("https://worldcat.org/isbn/9780141439518".into());
thing.identifiers = vec![id];
let masked = mask_thing(&thing);
assert!(masked.identifiers[0].url.is_none());
}
#[test]
fn test_mask_short_identifier() {
let mut thing = Thing::new("Test");
thing.identifiers = vec![ThingIdentifier::sku("AB")];
let masked = mask_thing(&thing);
assert_eq!(masked.identifiers[0].value, "****");
}
#[test]
fn test_mask_preserves_name() {
let thing = Thing::new("Pride and Prejudice");
let masked = mask_thing(&thing);
assert_eq!(masked.name, "Pride and Prejudice");
}
#[test]
fn test_mask_preserves_property_id() {
let mut thing = Thing::new("Test");
thing.identifiers = vec![ThingIdentifier::isbn("9780141439518")];
let masked = mask_thing(&thing);
assert_eq!(
masked.identifiers[0].property_id,
thing.identifiers[0].property_id
);
}
#[test]
fn test_mask_no_sensitive_fields() {
let thing = Thing::new("Public Thing");
let masked = mask_thing(&thing);
assert!(masked.owner.is_none());
assert!(masked.identifiers.is_empty());
}
#[test]
fn test_gdpr_export_preserves_fields() {
let mut thing = Thing::new("Export Test");
thing.description = Some("A test thing".into());
let export = gdpr_export(&thing);
assert_eq!(export["name"], "Export Test");
assert_eq!(export["description"], "A test thing");
}
#[test]
fn test_gdpr_export_has_all_top_level_fields() {
let thing = Thing::new("Full Export");
let export = gdpr_export(&thing);
for key in [
"id",
"name",
"alternate_names",
"description",
"disambiguating_description",
"additional_type",
"url",
"identifiers",
"images",
"main_entity_of_page",
"owner",
"same_as",
"subject_of",
"potential_action",
"is_deleted",
"created_at",
"updated_at",
] {
assert!(export.get(key).is_some(), "missing field: {key}");
}
}
}