feat(dummies): add the Any.Uri() family of URI generators#295
Merged
Conversation
Add Any.Uri(), an arbitrary-yet-valid System.Uri generator organised as a typed family. The unconstrained root spans the safe URI space — an absolute web (http/https), WebSocket (ws/wss), FTP or mailto URI, or a relative reference — and each narrowing (Web/WebSocket/Ftp/Mailto/Relative) returns a family-specific builder that exposes only that family's valid components, so an impossible combination (a port on a mailto, a fragment on a WebSocket) cannot be written rather than being caught at run time. Every component is drawn from ASCII-unreserved characters and the URI is assembled directly, so a value is valid by construction — never generated then filtered — and reproducible under a seed on every target framework. Internationalized (IDN) hosts are rejected (pass punycode) and the file scheme is kept out of the unconstrained draw, because a platform-dependent path would not round-trip identically across frameworks — the way Any.Double() keeps NaN and the infinities out of its default draw. Mirror the factory on AnyContext, declare the surface in both per-TFM public API baselines, and cover it with per-family valid-by-construction fuzzing, scheme and family reachability, the conflict cases, argument validation (IDN rejection, port and segment bounds) and seeded reproducibility. Refs: #226 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FPAdCRsiaVo2orjJwCaDnb
Two fixes for the not-yet-shipped Any.Uri family so it builds and validates identically down to the net472 floor: - AnyUriTests used string.Split(char, StringSplitOptions), an overload that only exists on .NET Core; switch to the char[] overload so the test compiles on net472. - RequireHost rejected shorthand-IPv4 hosts by round-tripping the host through System.Uri, whose shorthand parsing is itself what differs across target frameworks — so the guard was fragile in the same way as the input it rejects. Replace it with a framework-independent dotted-quad check (IsCanonicalIpv4) that never touches System.Uri. Refs: #226 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FPAdCRsiaVo2orjJwCaDnb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
Any.Uri(), an arbitrary-yet-validSystem.Urigenerator organised as a typed family. The unconstrained root spans the safe URI space — an absolute web (http/https), WebSocket (ws/wss), FTP or mailto URI, or a relative reference — and each narrowing (Web/WebSocket/Ftp/Mailto/Relative) returns a family-specific builder exposing only that family's valid components, so an impossible combination (a port on a mailto, a fragment on a WebSocket) cannot even be written. One item of the demand-driven Nice-to-Have backlog.Type of change
Changes
Any.Uri()→AnyUriroot with family narrowingsWeb()/WebSocket()/Ftp()/Mailto()/Relative(), each returning a typed builder (AnyWebUri,AnyWebSocketUri,AnyFtpUri,AnyMailtoUri,AnyRelativeUri) that exposes exactly that family's RFC-valid components (e.g.AnyWebSocketUrihas no user-info/fragment per RFC 6455;AnyMailtoUrihas no port).filescheme out of the unconstrained draw, the wayAny.Double()keeps NaN and the infinities out of its default draw.AnyContext, declare the surface in both per-TFM public-API baselines, and document it in the package README and XML docs.Testing
dotnet build FirstClassErrors.slndotnet test FirstClassErrors.slnFirstClassErrors.Analyzers.UnitTests)Full solution: 10 test projects, 0 failures (
Dummies.UnitTests494). Coverage: per-family valid-by-construction fuzzing, scheme and family reachability, the conflict cases, argument validation (IDN and non-canonical host rejection, port and segment bounds), the degenerate-relative case, and seeded reproducibility. Additionally validated by a multi-agent adversarial pass (~5.7M fuzzed generations plus RFC-surface, API-honesty and code-review agents); every real finding it surfaced (single-arg user-info password, shorthand-IPv4 host, degenerate 0-segment relative, culture-invariant path label) is fixed in this commit.Documentation
doc/updateddoc/handwritten/for-users/README.fr.md) updated if user-facing behavior changedThe Dummies package README and the XML docs are updated. The for-users
README.fr.mddocuments FirstClassErrors, not the Dummies constraint surface (the Dummies user guide is tracked separately), so no French translation applies here.Architecture decisions
This PR establishes the typed-progression API pattern — a builder that changes type and surface as it narrows — which is distinct from every other (flat, runtime-conflict) builder in the library. Rationale: URIs span structurally different shapes (authority vs mailto vs relative), so category errors (
Mailto().WithPort()) are made unrepresentable at compile time, whereas value-dependent conflicts (as inAnyString) can only be runtime. Per the maintainer's decision this is not recorded as an ADR yet; flagged here so it can become aProposedADR if desired.Related issues
Refs #226
🤖 Generated with Claude Code
https://claude.ai/code/session_01FPAdCRsiaVo2orjJwCaDnb
Generated by Claude Code