Skip to content

feat: add IpNetwork::to_ipv6_mapped and to_canonical for dual-stack conversion - #99

Merged
3Hren merged 1 commit into
mainfrom
feat/ipnetwork-family-conversion
Jul 13, 2026
Merged

feat: add IpNetwork::to_ipv6_mapped and to_canonical for dual-stack conversion#99
3Hren merged 1 commit into
mainfrom
feat/ipnetwork-family-conversion

Conversation

@3Hren

@3Hren 3Hren commented Jul 13, 2026

Copy link
Copy Markdown
Member

Closes an API gap on the IpNetwork enum: Ipv4Network has to_ipv6_mapped() and Ipv6Network has to_ipv4_mapped(), but the enum itself had no way to move between address families.

The motivating use case is dual-stack comparison. On the enum, contains/intersects return false/None across families by design, so lifting both operands into IPv6 space is the way to compare them meaningfully.

API

  • IpNetwork::to_ipv6_mapped(&self) -> Ipv6Network — embeds the network into IPv6 space. V4 delegates to Ipv4Network::to_ipv6_mapped(), V6 is the identity (the result is therefore not necessarily ::ffff:-prefixed).
  • IpNetwork::to_canonical(&self) -> IpNetwork — mirrors core::net::IpAddr::to_canonical. V4 passes through, V6 collapses to V4 when it is IPv4-mapped.
  • impl From<IpNetwork> for Ipv6Network, next to the existing From<Ipv4Network>.

Both methods are const fn, #[inline], #[must_use], and delegate to the existing conversion methods — no bit arithmetic is re-derived.

Ipv6Network::is_ipv4_mapped_ipv6 requires both the address to be ::ffff:-prefixed and the mask's top 96 bits to be all ones, so to_ipv4_mapped is the exact inverse of to_ipv6_mapped on the image of the embedding, and to_canonical never loses addresses.

Tests

12 unit tests and 9 property tests. Coverage includes non-contiguous masks in both families, the IPv4-compatible-but-not-mapped case, and the case where the address is ::ffff:-prefixed but the mask does not pin the top 96 bits (the network must stay V6 — collapsing it would drop addresses).

Two property tests pin the motivating use case: a.contains(&b) == a.to_ipv6_mapped().contains(&b.to_ipv6_mapped()), and likewise for intersects.

No benchmarks: these are two-arm delegating wrappers over already-benchmarked methods, so no new hot path is introduced.

@3Hren
3Hren merged commit 9489ef3 into main Jul 13, 2026
5 checks passed
@3Hren
3Hren deleted the feat/ipnetwork-family-conversion branch July 13, 2026 07:19
@3Hren 3Hren mentioned this pull request Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant