///|
pub(all) struct PlaceholderRegistry {
values : Map[String, String]
} derive(Debug, Eq)
///|
pub(all) struct PseudonymRegistry {
salt : String
values : Map[String, String]
} derive(Debug, Eq)
///|
pub fn PlaceholderRegistry::default() -> PlaceholderRegistry {
{ values: Map([]) }
}
///|
pub fn PseudonymRegistry::new(salt : String) -> PseudonymRegistry {
{ salt, values: Map([]) }
}
///|
pub fn PlaceholderRegistry::set(
self : PlaceholderRegistry,
kind : PhiKind,
value : String,
) -> Unit {
self.values[phi_kind_name(kind)] = value
}
///|
pub fn PlaceholderRegistry::get(
self : PlaceholderRegistry,
kind : PhiKind,
) -> String {
self.values.get_or_default(phi_kind_name(kind), "[\{phi_kind_name(kind)}]")
}
///|
pub fn PlaceholderRegistry::contains(
self : PlaceholderRegistry,
kind : PhiKind,
) -> Bool {
self.values.contains(phi_kind_name(kind))
}
///|
pub fn default_placeholders() -> PlaceholderRegistry {
let registry = PlaceholderRegistry::default()
registry.set(PersonName, "[NAME]")
registry.set(IdNumber, "[ID]")
registry.set(Phone, "[PHONE]")
registry.set(Date, "[DATE]")
registry.set(Email, "[EMAIL]")
registry.set(Address, "[ADDRESS]")
registry.set(MedicalRecord, "[MRN]")
registry.set(Insurance, "[INSURANCE]")
registry.set(Organization, "[ORG]")
registry
}
///|
pub fn registry_placeholders(
registry : PlaceholderRegistry,
kinds : Array[PhiKind],
) -> Map[String, String] {
let result : Map[String, String] = Map([])
for kind in kinds {
result[phi_kind_name(kind)] = registry.get(kind)
}
result
}
///|
pub fn PseudonymRegistry::token(
self : PseudonymRegistry,
value : String,
kind : PhiKind,
) -> String {
let key = phi_kind_name(kind) + "\u{1f}" + value
match self.values.get(key) {
Some(token) => token
None => {
let token = "\{phi_kind_name(kind)}_\{stable_hash(self.salt + key)}"
self.values[key] = token
token
}
}
}
///|
pub fn PseudonymRegistry::size(self : PseudonymRegistry) -> Int {
self.values.length()
}
///|
pub fn PseudonymRegistry::checksum(self : PseudonymRegistry) -> String {
let lines = []
for key, value in self.values {
lines.push(key + "=" + value)
}
lines.sort()
stable_hash(lines.join("\n"))
}
///|
pub fn findings_with_placeholders(
findings : Array[Finding],
registry : PlaceholderRegistry,
) -> Array[Finding] {
findings.map(fn(item) { { ..item, replacement: registry.get(item.kind) } })
}
///|
pub fn redact_with_placeholders(
input : String,
registry : PlaceholderRegistry,
) -> DeidResult raise DeidError {
let findings = findings_with_placeholders(scan(input), registry)
let (text, offsets) = apply_findings(input, findings)
{
text,
findings,
offsets,
audit: build_audit(input, text, findings, offsets),
}
}
///|
pub fn redact_with_pseudonyms(
input : String,
salt : String,
) -> DeidResult raise DeidError {
let registry = PseudonymRegistry::new(salt)
let findings = scan(input).map(fn(item) {
{ ..item, replacement: registry.token(item.text, item.kind) }
})
let (text, offsets) = apply_findings(input, findings)
{
text,
findings,
offsets,
audit: build_audit(input, text, findings, offsets),
}
}
///|
pub fn redact_with_kind_prefixes(
input : String,
prefix : String,
) -> DeidResult raise DeidError {
let findings = scan(input).map(fn(item) {
{ ..item, replacement: prefix + "[" + phi_kind_name(item.kind) + "]" }
})
let (text, offsets) = apply_findings(input, findings)
{
text,
findings,
offsets,
audit: build_audit(input, text, findings, offsets),
}
}
///|
pub fn replacement_dictionary(findings : Array[Finding]) -> Map[String, String] {
let result : Map[String, String] = Map([])
for item in findings {
result[item.text] = item.replacement
}
result
}
///|
pub fn replacement_dictionary_checksum(findings : Array[Finding]) -> String {
let dictionary = replacement_dictionary(findings)
let lines = []
for key, value in dictionary {
lines.push(key + "=" + value)
}
lines.sort()
stable_hash(lines.join("\n"))
}