--- title: "How pslr compares to other PSL libraries" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{How pslr compares to other PSL libraries} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The [Public Suffix List website](https://publicsuffix.org/learn/) catalogs implementations in C, C#, C++, Go, Haskell, Java, JavaScript, Perl, PHP, Python, Ruby, Rust, Swift, and more — but no R. `pslr` fills that gap, and it is built to be a *reproducibility- and correctness-first* engine rather than a quick suffix splitter. The table compares `pslr` with a representative set of the most established libraries from that catalog, across the dimensions that matter for correct, auditable suffix handling. ✅ first-class · ◐ partial/limited · ⚠ with a caveat · ❌ absent. | Library (language) | Full algorithm (`*`/`!`) | ICANN / PRIVATE / both | IDN + Punycode | Offline default + explicit refresh | Queryable provenance | Compiled core | Strict input validation | Unlisted-TLD policy configurable | |---|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:| | **pslr (R)** | ✅ | ✅ 3-way | ✅ in & out | ✅ bundled; validated HTTPS-only `psl_refresh()` | ✅ list **and** normalization identity | ✅ `cpp11` | ✅ rejects URLs, IPv4, IPv6 | ✅ `unknown=` | | libpsl (C) | ✅ | ✅ 3-way | ✅ | ✅ bundled; OS-package update | ◐ sha1 + mtime | ✅ | ◐ | ✅ | | x/net/publicsuffix (Go) | ✅ | ◐ `icann` flag | ❌ ASCII only | ✅ embedded; bump module | ◐ date constant | ✅ | ◐ | ❌ | | publicsuffix-go (Go) | ✅ | ◐ toggle private | ✅ | ✅ embedded | ❌ | ❌ | ◐ | ✅ | | psl crate (Rust) | ✅ | ◐ per-suffix type | ✅ | ✅ compiled-in | ◐ dated releases | ✅ | ❌ | ◐ | | tldextract (Python) | ✅ | ◐ private on/off | ✅ | ⚠ network-first; offline opt-in | ❌ | ❌ | ◐ | ❌ | | publicsuffixlist (Python) | ✅ | ◐ exclude private | ✅ | ✅ bundled + updater | ◐ date in version | ❌ | ❌ | ❌ | | public_suffix (Ruby) | ✅ | ◐ `ignore_private` | ❌ caller pre-encodes | ✅ bundled | ❌ | ❌ | ◐ | ✅ | | php-domain-parser (PHP) | ✅ | ✅ 3-way | ✅ | ⚠ not bundled; PSR-16 cache | ◐ `isKnown` flags | ❌ | ◐ | ◐ | | tldts (JS/TS) | ✅ | ◐ `allowPrivateDomains` | ✅ | ✅ embedded; npm bump | ◐ submodule pin | ◐ optional WASM | ✅ | ◐ | | Guava `InternetDomainName` (Java) | ◐ | ✅ registry vs public | ✅ | ◐ in-jar; bump Guava | ❌ | ❌ | ◐ | ❌ | ## What `pslr` does differently - **Provenance is best-in-class.** `psl_version()` records the list identity (source, commit, date, SHA-256) *and* the normalization identity (normalizer package + version, profile, Unicode version). A PSL answer depends on both *which list* answered and *how the host was normalized* — `pslr` is the only surveyed library that surfaces both, so a result is genuinely reproducible. - **Conformance is verified.** The package ships the official upstream `tests.txt` vectors, pinned in lockstep with the bundled snapshot and run on every check. - **Offline by default, one hardened network path.** Every query works with zero network access; `psl_refresh()` is the *only* code that touches the network — HTTPS-only, credential- and downgrade-rejecting, size-capped, conditional when it can be, published append-only. This is stricter than network-first designs and pairs with the reproducibility story rather than fighting it. - **Freshness reported honestly.** `psl_status()` separates "a check is due" from "a newer list was actually observed", and never infers the second from snapshot age. Surveyed libraries report a date or nothing at all. - **Snapshots can be diffed.** Because every retained download is an immutable, checksummed artifact, `psl_diff()` reports what actually changed between two snapshots — canonically, offline, keyed on each rule's logical identity so a wildcard/exception change or an ICANN/PRIVATE move reads as one change rather than an unrelated removal and addition. No surveyed library offers this. - **Strict, policy-driven validation.** URLs, IPv6, dotted-decimal IPv4, and malformed labels are rejected; `invalid = "na"` / `"error"` lets you choose silent `NA` or a hard stop. Many libraries are deliberately lenient. - **Vectorized, `NA`-safe, name-preserving.** Every function operates on a whole character vector — the idiomatic shape for data work in R. ## Trade-offs - **Session-global default list.** Queries read the per-session active list (`psl_use()` / `psl_refresh()`) unless a call passes `engine =`, a `psl_engine()` built for one specific snapshot. - **Hostnames, not URLs.** URL-shaped input is rejected by design; parse the host out first or use [`rurl`](https://CRAN.R-project.org/package=rurl). - **No network-first auto-fetch.** Refreshing is always explicit — a deliberate choice for reproducibility, not a convenience feature.