domain: UserName regex and webmention Selectors are recompiled on every call #70
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
rosa/vernier#70
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during a code review of the
domaincrate.Location:
crates/domain/src/models/user.rs:43(UserName::parse);crates/domain/src/models/webmention.rs:88and:368Type: Cleanup — wasted work
Problem
UserName::parsecompilesRegex::new(r"^[a-z0-9]+$")on every call (the crate only regex use), andscrape_targets/classify_mentionrebuildSelector::parse("a")/Selector::parse("a[href]")on every call. All are constants.Cost
Per-call regex/selector construction on paths hit for every username parse and every webmention scrape (twice per post update).
Suggested fix
Hoist the selectors into
static ... LazyLock<Selector>. For the username check, either aLazyLock<Regex>or, simpler, drop the regex fortrimmed.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit())(the following length check already rejects empty), and drop theregexdependency.Agent Brief
Category: enhancement
Summary: Stop recompiling constant regexes/selectors on every call; construct them once (or eliminate them)
Current behavior:
Three call paths rebuild compile-time-constant patterns on every invocation:
UserName::parsecompiles the^[a-z0-9]+$regex each time it runs. It is the domain crate's only use ofregex.scrape_targetsrebuilds theaselector andclassify_mentionrebuilds thea[href]selector on every call. Both run for every webmention scrape (twice per post update).link[rel=webmention]anda[rel=webmention]selectors for every fetched target.Desired behavior:
Constant patterns are constructed at most once for the life of the process, or eliminated. The repo already has the idiom to copy:
static ... : LazyLock<...>as used forUSERNAME_REGEXin the web crate's form extractors and for the processed stylesheet.For
UserName::parse, prefer the simpler option from the issue: drop the regex entirely in favor of a character check liketrimmed.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()), and remove theregexdependency from the domain crate's manifest (the workspace keepsregexfor the web crate). Note one subtlety:chars().all(...)is vacuously true for an empty string, so empty/whitespace-only input would then be rejected by the existing minimum-length check instead of the charset check — the issue explicitly accepts this; the input must still be rejected either way.Key interfaces:
UserName::parse— accepted/rejected inputs are unchanged: trim, lowercase, only[a-z0-9], length 3–64. No signature change.scrape_targetsandclassify_mentionin the domain webmention model — behavior and signatures unchanged; only the selector construction moves.link[rel=webmention], thena[rel=webmention]); only the selector construction moves.Acceptance criteria:
Regex::neworSelector::parseof a constant pattern remains in the domain crate or the infra webmention senderUserName::parseaccepts and rejects the same set of inputs as before (empty and whitespace-only input still rejected)regexciandclippymise tasks passOut of scope:
Html::parse_document/parse_fragmentcalls — those parse per-input HTML and are not constantsLazyLock