use rete_core::iri::IriReport;
fn class_rows(report: &IriReport, indent: &str, with_examples: bool) {
for (defect, count, sample) in report.classes() {
let tail = if defect.repairable() {
""
} else {
" [NOT repairable by escaping]"
};
eprintln!("{indent}{count:>7} {}{tail}", defect.reason());
if with_examples {
if let Some(s) = sample {
eprintln!("{indent} e.g. {}", elide(s));
}
}
}
}
fn elide(token: &str) -> String {
const MAX: usize = 96;
if token.chars().count() <= MAX {
return token.to_string();
}
let head: String = token.chars().take(MAX - 1).collect();
format!("{head}…")
}
pub(crate) fn warn_after_build(report: &IriReport) {
if report.is_empty() {
return;
}
eprintln!(
"warning: {} statement(s) carry an invalid IRI ({} IRI occurrence(s)).",
report.statements(),
report.occurrences()
);
eprintln!(
" They are stored verbatim, so `rete export --format nq` emits N-Quads that a"
);
eprintln!(
" strict parser (Oxigraph, Jena, rapper) rejects — and a bulk loader rejects the"
);
eprintln!(" whole chunk, not the line.");
class_rows(report, " ", true);
eprintln!(" `rete export --sanitize-iris` percent-encodes the repairable ones (which");
eprintln!(" changes the IRI); `rete build --strict` refuses such input outright.");
}
pub(crate) fn report_sanitized(report: &IriReport) {
if report.is_empty() {
eprintln!("--sanitize-iris: totals invalid=0 repairable=0 unrepairable=0 unclassified=0");
eprintln!(
"--sanitize-iris: no invalid IRIs found; the dump is byte-identical to a plain export."
);
return;
}
eprintln!(
"--sanitize-iris: totals invalid={} repairable={} unrepairable={} unclassified={}",
report.occurrences(),
report.repairable(),
report.unrepairable(),
report.unclassified()
);
if report.repaired() > 0 {
eprintln!(
"--sanitize-iris: percent-encoded {} IRI occurrence(s). The dump's IRIs are NOT the",
report.repaired()
);
eprintln!(" file's IRIs: it no longer joins against the source graph, and");
eprintln!(" rete → store → rete is no longer the identity.");
for (defect, count, _) in report.classes().filter(|(d, _, _)| d.repairable()) {
eprintln!(" {count:>7} {}", defect.reason());
}
}
if report.unrepairable() > 0 {
eprintln!(
"--sanitize-iris: {} occurrence(s) CANNOT be repaired by escaping and were written",
report.unrepairable()
);
eprintln!(
" verbatim — this dump is still not valid N-Quads. Fix them at the"
);
eprintln!(
" source; a relative IRI needs a base IRI the file never recorded."
);
for (defect, count, sample) in report.classes().filter(|(d, _, _)| !d.repairable()) {
eprintln!(" {count:>7} {}", defect.reason());
if let Some(s) = sample {
eprintln!(" e.g. {}", elide(s));
}
}
if report.unclassified() > 0 {
eprintln!(
" The unclassified ones are invalid by RFC 3987 — the parser this"
);
eprintln!(
" tool shares with Oxigraph's N-Triples reader refused them — but"
);
eprintln!(
" match no repair rete knows, so nothing was changed. `<https://::1>`"
);
eprintln!(
" (an IPv6 host missing its brackets) is the shape that taught us to"
);
eprintln!(" count these rather than pass them.");
}
}
}