///|
fn count_key(kind : PhiKind) -> String {
"\{kind}"
}
///|
fn add_count(counts : Map[String, Int], kind : PhiKind) -> Unit {
let key = count_key(kind)
counts[key] = match counts.get(key) {
Some(v) => v + 1
None => 1
}
}
///|
fn apply_findings(
input : String,
findings : Array[Finding],
) -> (String, Array[OffsetMap]) {
let output = StringBuilder()
let offsets = []
let mut cursor = 0
for finding in findings {
if finding.start >= cursor && finding.end <= input.length() {
output.write_string(input[cursor:finding.start].to_owned())
let replacement_start = output.to_string().length()
output.write_string(finding.replacement)
let replacement_end = output.to_string().length()
offsets.push({
original_start: finding.start,
original_end: finding.end,
replacement_start,
replacement_end,
finding_id: finding.id,
})
cursor = finding.end
}
}
output.write_string(input[cursor:].to_owned())
(output.to_string(), offsets)
}
///|
fn build_audit(
input : String,
output : String,
findings : Array[Finding],
offsets : Array[OffsetMap],
) -> AuditReport {
let counts : Map[String, Int] = Map([])
for finding in findings {
add_count(counts, finding.kind)
}
{
input_length: input.length(),
output_length: output.length(),
finding_count: findings.length(),
applied_count: offsets.length(),
counts,
findings,
offsets,
}
}
///|
pub fn redact(
input : String,
rules? : Array[Rule] = comprehensive_rules(),
options? : DeidOptions = DeidOptions::default(),
) -> DeidResult raise DeidError {
let findings = scan(input, rules~, options~)
let (text, offsets) = apply_findings(input, findings)
{
text,
findings,
offsets,
audit: build_audit(input, text, findings, offsets),
}
}
///|
pub fn audit_markdown(report : AuditReport) -> String {
let lines = [
"# moonbit-deid audit",
"",
"- input_length: \{report.input_length}",
"- output_length: \{report.output_length}",
"- findings: \{report.finding_count}",
"- applied: \{report.applied_count}",
"",
"## counts",
]
for k, v in report.counts {
lines.push("- \{k}: \{v}")
}
lines.push("")
lines.push("## findings")
for f in report.findings {
lines.push(
"- \{f.id} \{f.kind} \{f.start}..\{f.end} via \{f.rule_id} confidence=\{f.confidence}",
)
}
lines.join("\n")
}